Terminology:
A default constructor is a constructor that can be called with no arguments. For instance:
class A { A() { /* ... */ } };
class Point { Point(int x = 0, int y = 0) { /* ... */ } };
A copy constructor is a constructor whose "first parameter is of type X&
, const X&
,
volatile X&
or const volatile X&
, and either there are no other parameters or else all other parameters have default arguments". For instance:
class A { A(const A& other) { /* ... */ } };
class Point { Point(Point& other, int x = 0) { /* ... */ } };
If your class declares no constructor, the compiler will implicitly define a default constructor for you. The implicitly defined default constructor performs only very basic initializations:
- Any base-class subobject is default-constructed;
- Any member of class type is default-constructed;
- No initialization is performed for class members of scalar type (
int
, double
, pointers, etc.)
If any of the above is impossible (for example, a member of class type has no default constructor), then the compiler will report an error.
If your class does not declare a copy constructor, one is implicitly declared. If a move constructor or move assignment operator is declared, the copy constructor is declared as deleted and any attempt to use it will result in a compiler error. Otherwise, the implicitly declared copy constructor performs a memberwise copy.
Second, unlike Java, C++ objects have value semantics. When you write Animal animal;
in Java, animal
is a reference to an Animal
object and is initialized to a null reference. When you write Animal animal;
in C++, animal
is an Animal
object. When you write Animal animal2 = animal;
in Java, animal2
and animal
refer to the same Animal
object, and you just copied the reference. When you write Animal animal2 = animal;
in C++, animal2
is a copy of animal
created using the copy constructor. They are distinct Animal
objects. (The Java equivalent for this is clone()
.)
Further, new
in C++ and in Java are very different things. In Java, new
is how you create an object. In C++, as noted above, we don't need new
to create an object. Rather, new
means "allocate memory on the heap, construct the object in that memory, and return a pointer to it, which I'll free with delete
when I'm done". new Animal()
doesn't return an Animal
, it returns an Animal *
- a pointer to an Animal
. Thus, something like animals[0] = new Animal();
will never compile without some horrible hackery, and even if you somehow made it compile, you've lost the pointer returned by new
, so you cannot delete
it, which means that you now have a memory leak. It's just terrible C++ code. Don't do it.
Now, when you define an array in C++ without an initializer:
Animal animals[10];
You create an array of 10 Animal
objects, not 10 references to Animal
objects. Each member of the array is default-constructed. If Animal
does not declare (explicitly or implicitly) a default constructor, you'll get a compiler error. The exact initialization performed depends on the default constructor; if you use the implicit one, then scalar class members will have indeterminate values and attempting to read the value results in undefined behavior.
The proper way of ensuring that default-constructed Animal
s do not have uninitialized members is to simply write your own default constructor that performs the necessary initialization:
class Animal {
public:
Animal() : m_name("none"), m_legs(0) { }
private:
std::string m_name;
int m_legs;
};
This default constructor initializes the name of the animal to the string "none"
and the number of legs to 0
.
Arrays in C++ do not carry their size information with them. There are several ways to get around this:
- Use the
std::array
class, which provides a size()
member function, instead of a plain array. Instead of Animal animals[10];
, write std::array<Animal, 10> animals;
. Then you can get the size with animals.size()
. This is the best way by far.
- Calculate the size of the array with
sizeof(animals) / sizeof(animals[0])
. This would not work if you pass animals
to another function (unless the function is written in a very special way), since arrays will decay to pointers at the drop of a hat, and calling sizeof
on a pointer won't give you the array's size.
- Store the size as a constant:
const int szAnimals = 10; Animal animals[szAnimals];
.