6

Consider this piece of code:

#include <vector>
#include <iostream>
using namespace std;

class Base
{
    char _type;
public:
    Base(char type):
        _type(type)
    {}

    ~Base() {
        cout << "Base destructor: " << _type << endl;
    }
};

class uncopyable
{
    protected:
        uncopyable() {}
        ~uncopyable() {}
    private:
        uncopyable( const uncopyable& );
        const uncopyable& operator=( const uncopyable& );
};

class Child : public Base, private uncopyable
{
    int j;
public:
    Child():
        Base('c')
    {}
    ~Child() {
        cout << "Child destructor" << endl;
    }
};


int main()
{
    vector<Base> v;
    Base b('b');
    Child c;

    v.push_back(b);
    v.push_back(c);
    return 0;
}

The output on my system is:

Base destructor: b
Child destructor
Base destructor: c
Base destructor: b
Base destructor: b
Base destructor: c

My questions are:

  • Why is the destructor of Base (with type b) called three times instead of two (do we have more than two copies of object b)?

  • What happens when we copy an object of type Child, considering the copy-constructor of one of its parents is private. Is it undefined behavior?

  • I was expecting to get a compile-time error whenever I try to copy an object of type Child. I thought the child's default copy-constructor would try to call the private copy-constructor of the Uncopyable class and cause compilation error. Why doesn't it give compile errors?

The reason the code is designed this way is because the Child class is huge.

The desired behavior is throwing away the child data whenever a client tries to copy a Child object (calling the destructor of Child without calling the destructor of Base).

This piece of code achieves that, but I guess it results in undefined behavior and has memory leak (never calls the destructor of Child for the copied instance).

Shayan Pooya
  • 1,049
  • 1
  • 13
  • 22
  • Do you want a `compile-time` error or do you want to throw away Child's data? These are conflicting statements, unless I'm missing something. – Chip May 19 '12 at 01:01

3 Answers3

9

Here is what happens in your code:

int main() 
{ 
    vector<Base> v;    // 1
    Base b('b');       // 2
    Child c;           // 3

    v.push_back(b);    // 4
    v.push_back(c);    // 5
    return 0; 
}                      // 6
  1. line 1: vector v constructed

  2. line 2: Base b constructed (calling Base's constructor)

  3. line 3: Child c constructed (calling Child's constructor and Base's constructor)

  4. line 4: v is current at maximum capacity and needs to be resized.
    Memory is allocated for 1 element of Base by v.
    Base b copied into v[0] (calling Base's copy constructor).

  5. line 5: v is again at maximum capacity and needs to be resized.
    Memory is allocated for 2 elements of Base by v.
    The old v[0] is copied into the new v[0] (calling Base's copy constructor).
    The old v[0] is deleted (calling Base's destructor ("Base destructor: b")).
    Child c is copied into v[1] (calling Base's copy constructor).

  6. line 6: c, b, and v run out of scope.
    Child c is deleted (calling Child's destructor ("Child destructor") then Base's destructor ("Base destructor: c").
    Base b is deleted (calling Base's destructor ("Base destructor: b")).
    Base v[0], v[1] are deleted (calling Base's destructor twice ("Base destructor: b", "Base destructor: c")).

There is no memory leak - for every constructor in the above sequence a corresponding destructor is called.

Additionally, you seem to be very confused about copy constructors. Child c gets passed to push_back as a Base& - which then calls Base's copy constructor as expected. Since Base's implicit copy constructor is not virtual or overriden, having Child derive from uncopyable does nothing to change this.

Note that a vector<Base> cannot ever store an object of type Child; it only knows to allocate enough memory for a Base. What occurs when assigning an instance of Child to a Base is known as slicing, which, while often unintended and misunderstood, seems like it may actually be what you want in your described scenario.

imasloen
  • 166
  • 2
3

I was expecting to get a compile-time error whenever I try to copy an object of type Child.

You aren't copying a Child object. When you put Child c into a vector<Base>, only the Base gets copied. It's basically the same as executing b = c;. If you do copy/assign Child you will get an error.

Child d = c;  // compile error

The default copy constructor will call the copy constructors of any base class and member objects and do a bitwise copy for primitives and pointers.

xan
  • 7,511
  • 2
  • 32
  • 45
  • Thanks. This seems correct. What about the extra call to Base's destructor? – Shayan Pooya May 19 '12 at 01:35
  • 1
    @Shayan, it's presumably an extra copy vector<> made while reallocating its storage. Different vector<> impls may behave differently if they over allocate. – xan May 19 '12 at 03:10
0

EDIT: The answer is wrong.. currently editing to a better response.

Why is the destructor of Base (with type b) called three times instead of two (do we have more than two copies of object b)?

Most likely is that the Vector is making a copy of b. Vectors do that often.

What happens when we copy an object of type Child, considering the copy-constructor of one of its parents is private. Is it undefined behavior?

No. The copy constructor of C will call the copy constructor of the base classes. So if base class copy constructors are private, it will not compile.

I need to get a compile-time error whenever I try to copy an object of type Child while, permitting copying of Base class objects. What is the best way to do that?

Declare a private copy-constructor for Child like so:

private:
    Child(const Child& a) {
        throw "cannot make a copy";
    } 

The desired behavior is throwing away the child data whenever a client tries to copy a Child object (calling the destructor of Child without calling the destructor of Base).

Not sure what you mean. The copy constructor means creating a new object. You cannot do operations on the (old) object.

Chip
  • 3,226
  • 23
  • 31
  • 1. IMHO vector should copy it once. Not twice. 2. IMHO the implicit copy-constructor of the child should call the copy-constructor of the parent(s). http://stackoverflow.com/questions/9178204/why-does-the-implicit-copy-constructor-calls-the-base-class-copy-constructor-and 3. It does not work (you can try it). Because the object is first type-cast to parent and then copied. – Shayan Pooya May 19 '12 at 01:07
  • In the suggested code, we are creating a new Child, and just not copying the data. It is defeating the purpose of not creaing a new object again. – Shayan Pooya May 19 '12 at 01:13
  • I think I'm missing something. The copy constructor will be called when you are creating an object from another object. That's what a copy constructor does... Could you explain what you want to do? – Chip May 19 '12 at 01:17
  • Actually you are right.. I will edit the answer.. I will have to come back to this. – Chip May 19 '12 at 01:21