1

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.

  • 2
    _"B does not have a copy-constructor"_ isn't true. `B` has an [implicitly declared copy constructor](https://en.cppreference.com/w/cpp/language/copy_constructor) – Brian61354270 Jan 26 '22 at 21:43
  • 1
    Since you seem to be dabbling with resource management for the first time, here's some obligatory reading for handling resource management correctly: [C++ core guidelines C.21 "rule of five"](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rc-five), [cppreference rule of five](https://en.cppreference.com/w/cpp/language/rule_of_three), [What is the Rule of Three?](https://stackoverflow.com/q/4172722/11082165), [What is move semantics?](https://stackoverflow.com/q/3106110/11082165) – Brian61354270 Jan 26 '22 at 21:47
  • @Brian Thanks, I will read it all. 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, so how come A -dtor appears twice in the output? – DirichletIsaPartyPooper Jan 26 '22 at 21:51
  • 3
    _"how come A -dtor appears twice in the output?"_ you `delete` the same `A` object twice, and are seeing Undefined Behavior. This is because your classes violate [The Rule of Three](https://stackoverflow.com/a/4172961/16287). – Drew Dormann Jan 26 '22 at 21:53
  • 1
    Default copy constructor does exactly that: It'll copy the pointer, not the stuff at the pointer. – user4581301 Jan 26 '22 at 21:53

1 Answers1

3

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.

eerorika
  • 232,697
  • 12
  • 197
  • 326