Reputation: 273
Consider the following program:
using namespace std;
class A{
private:
int _a;
public:
A(int a): _a(a) {cout<<"A-ctor: a= "<<_a<<endl;}
A(const A& other) : _a(other._a) {
cout<< " A-copy ctor: _a= " << _a<< endl;
}
~A() {cout << "A-dtor" << endl;}
};
class B{
private:
A* _aPtr;
public:
B(int a=0) : _aPtr(new A(a)) { cout << "B-ctor"<<endl;}
~B() {cout<<"B-dot"<<endl; delete _aPtr;}
};
class C:public B{
public:
C(int a=5) : B(a) {cout<< "C-ctor"<<endl;}
C(const C& other) : B(other) {cout<< "C-copy ctor"<<endl;}
~C() {cout<<"C-dtor"<<endl;}
};
int main()
{
C c1;
C c2(c1);
return 0;
}
I would expect a compilation error since B
does not have a copy-constructor and we try to use with C c2(c1)
But the actual output is:
A-ctor: a= 5
B-ctor
C-ctor
C-copy ctor
C-dtor
B-dot
A-dtor
C-dtor
B-dot
A-dtor
Why there is no compilation error here?
Thanks in advance.
Upvotes: 1
Views: 80
Reputation: 238351
I would expect a compilation error since B does not have a copy-constructor
But B
does have a copy-constructor. Its copy constructor is implicitly defined.
Why there is no compilation error here?
Because the program is well-formed.
But even if it has a copy constructor, I suppose the copy would be shallow, meaning the pointer field of the instances that was passed by reference would be copy in a shallow way and basically would point to the same location in the heap memory
Correct.
so how come A -dtor appears twice in the output?
Once the second C
object, and its B
base sub object is destroyed, the same pointer that had been invalidated by the destructor of the first B
object will be deleted again. The consequence is that the behaviour of the program is undefined. The program is broken; don't do this.
When you release a resource - such as dynamic memory - in a user defined destructor, you typically have to implement the copy and move assignment operators and constructors, because the implicit definitions would have counter-productive behaviour. This is known as the rule of 5 (known as rule of 3 prior to C++11). You have violated the rule of 5 by relying on the implicit constructor despite releasing dynamic memory in the destructor.
More generally, I recommend studying the RAII idiom. Furthermore I recommend that you avoid owning bare pointers such as _aPtr
and use smart pointers such as std::unique_ptr
instead. Lastly, I recommend avoiding unnecessary dynamic allocation.
Upvotes: 3