-2

I want to make sure I understand this.

Lets say I have a class Animal and I want to make an array of Animals.

I can for example have

    Animal animals[10]; 

This gives me an array of Animals, when doing this the default copy constructor is called and I now have 10 empty Animal objects.

Now if I want to start to "initialize" them (i only know java) I can have something like

    for(int i=0; i<animals.length; i++){
         animals[i] -> hasFur(true);
         Animal -> incCount();
    }

This would then give me ten Animals in an array which all have fur, and I would increment an animal counter to 10.

When you say:

   Animal animals[10];

what are the values initialized to?

Michael Miner
  • 964
  • 2
  • 17
  • 39
  • 1
    What is your question? – Code-Apprentice Jul 07 '14 at 03:39
  • just if my understanding is correct. And if my syntax is correct. I said this calls the copy constructor to make each object, I want to make sure that is correct as well. – Michael Miner Jul 07 '14 at 03:41
  • 1
    The compiler will not complain about the syntax. But it will complain that `animals` has no member named `length`. You also need to keep track yourself of the array's size. Use a std::array, and its size() method. – DavidO Jul 07 '14 at 03:42
  • with `Animal animals[10];` it could either be that 10 elements are default-constructed; or it could construct one and copy-construct the others from it. – M.M Jul 07 '14 at 03:49
  • 1
    @MattMcNabb Um, no, they must all be default-constructed. – T.C. Jul 07 '14 at 03:49
  • #T.C. so am I correct that, if no copy constructor is made in Animal then the default copy constructor will be called each time for each new Animal instance? – Michael Miner Jul 07 '14 at 03:51
  • @MichaelMiner There's "default constructor" and there's "copy constructor". They are different things. – T.C. Jul 07 '14 at 04:03
  • @T.C. ok so the default is called, now when the array is made, what are the animals initialized to? does C++ automatically set them to NULL? – Michael Miner Jul 07 '14 at 04:06
  • @MichaelMiner No, C++ has pointer types separate from non-pointer types and `NULL` only applies to pointers. – David Young Jul 07 '14 at 04:30
  • @TC OK, I was thinking of `std::vector animals(10);` . – M.M Jul 07 '14 at 04:46

4 Answers4

4

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 Animals 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];.
T.C.
  • 133,968
  • 17
  • 288
  • 421
1

Here is example code that would do what you are looking for (where hasFur is defaulted to false when the array is initially defined as per the default constructor defined below); however, it is unclear what is being requested by your reference to a incCount() routine. Note that neither std::array nor std:vector have a length method which is instead accomplished with the size (std::array std::vector)

#include <array>
#include <iostream>

using namespace std;

class Animal
{
private:
    bool __hasFur;

public:
    Animal(bool hasFur = false)
    {
        this->__hasFur = hasFur;
    }

    void hasFur(bool hasFur)
    {
        this->__hasFur = hasFur;
    }
};

int main (int argc, char ** argv)
{
    array<Animal, 10> animals;

    for(size_t i = 0; i < animals.size(); i++)
    {
         animals[i].hasFur(true);
         //Animal.incCount();
    }
    return 0;
}

What is the intended purpose of the incCount() method?

localhost
  • 375
  • 1
  • 3
  • 15
  • it would just be a static variable in the Animal class to track how many animals there are. In case you wanted to know. – Michael Miner Jul 07 '14 at 03:49
  • 1. `max_size()` will almost certainly segfault you. 2. Both `std::vector` and `std::array` have `size()`, which is what should be used. Neither has `length`. – T.C. Jul 07 '14 at 03:53
  • Initially in my testing it seemed like `size` did not work (due to an encountered warning), initially, even though it was defined; I tested again and found that it does so I fixed this in the code. However, in Visual Studio 2012 it does generate a warning, in fact with either `size` or `max_size`, the `int(animals.size())` is still necessary to eliminate warnings that occur. Essentially, the incCount() method is not necessary as it is obtained through the `size` routine. The warning, within the int cast, reads as follows: `warning C4018: '<' : signed/unsigned mismatch` – localhost Jul 07 '14 at 04:00
  • The above is for #1, in terms of #2, I confused with other std elements, that has been corrected in the code as well. Thanks! – localhost Jul 07 '14 at 04:06
  • `int(animals.size())` is a bad idea. `animals.size()` is better. If you're doing it to shut up a compiler warning, it would be preferable to heed the warning message! (i.e. use `size_t` instead of `int`). – M.M Jul 07 '14 at 04:47
  • @MattMcNabb Good point, if `size_t` is used for the counter variable than the warning goes away completely, and the int cast is not necessary at all and can be eliminated. I should also add that this relates to http://stackoverflow.com/questions/8188401/c-warning-c4018-signed-unsigned-mismatch Thanks! – localhost Jul 07 '14 at 04:52
1

C++ does not have an associated length property for arrays. (See How do I find the length of an array?)

You can do something like this:

int length = sizeof(animals)/sizeof(animals[0]);

Then, before accessing member variables of elements (like this: animals[i] -> hasFur(true);), you need to first create objects.

animals[i] = new Animal(); // assuming the constructor has no arguments

[Edit: not necessarily, see this answer]

Also, Amimal -> incCount() looks a bad idea to me. You can increment the counter in your constructor. This way ensures that you don't accidentally call incCount() and mess up the count of your static variable.

Community
  • 1
  • 1
sampathsris
  • 21,564
  • 12
  • 71
  • 98
0

I don't think you can do animals.length() in C++, I think your "for" need to be something like this:

for(int i=0; i<=10; i++){
     animals[i].hasFur(true);
     Animal.incCount();
}

About to what the array of Animal was initialized to: It depends on the class constructor. The default constructor just create the objects but don't assign any value to the variables, so unless you declared a custom constructor, the variables of the object would be set at random values.

Rodrigo
  • 100
  • 6
  • do you happen to know what the original array of 10 Animals is initialized to? – Michael Miner Jul 07 '14 at 03:56
  • It depends on the class constructor. The default constructor just create the objects but don't assign any value to the variables, so unless you declared a custom constructor, the variables of the object would be set at random values. – Rodrigo Jul 07 '14 at 04:22
  • @Rodrigo: Then explain that in the answer. Otherwise you have created a subtle trap for someone to fall in. – sampathsris Jul 07 '14 at 04:27