Search code examples
c++copy-constructorassignment-operatordeep-copy

Object not deleted before new is assigned


I'm kind of confused, because I was sure this should work different. Take a look at this code example:

#include <iostream>
#include <string>

using namespace std;

class base
{
    public:
    virtual ~base() = default;
};

class derived : public base
{
    private:
    int a = 0;
    int *b = nullptr;
    std::string lol;

    public:
    derived(std::string s) : b(new int(6)), lol{s} { cout << "ctor " << lol << endl; }
    derived(derived const& d) : lol{d.lol + " copy"} {cout << "copy " << lol << endl; }

    virtual ~derived() { cout << "dtor " << lol << endl; delete b; }

    virtual void superFunction() { cout << "OMG " << lol << endl; }
};

int main()
{
    derived a("a");
    derived b("b");
    a = b;
}

And the program output with all optimizations off is:

ctor a
ctor b
dtor b
dtor b

I was sure that in this case compiler should generate code that deletes object a and uses copy constructor to create new object. Instead it uses operator= that it implicitly declares.

Can someone explain why? Or point me to C++ standard.

Thanks.


Solution

  • When you write a = b;, compiler calls assignment operator, which will be automatically generated if not present in the code and not marked as deleted. Copy constructor is used only if you try to initialize a new object from another object like this:

    derived a("a");
    derived b = a;
    

    Also, your code crashes before main returns as it tries to delete b, which points to the same memory from a and from b after a = b; default-assignment.

    If you want to delete a with derived destructor after a = b; execution, all you need is copy-and-swap idiom. What is the copy and swap idiom? has a great answer on how to do that in legacy and modern C++. Proper implementation of rule-of-four from that answer will perfectly fit the DRY principle and help you to avoid memory issues. Note the fabulous trick with passing parameter to operator= by value, which makes compiler select the appropriate constructor (copy or move) and allows you to write only four methods instead of all five of them.