user2138149
user2138149

Reputation: 17416

C++ Inheritance and copy constructors, for non-trivial classes (deep copy)

On request by M.M I have re-posted this question.

This question is about inheritance, not the rule of 3, as YSC previously thought.

I have a class (two, actually)

class A
{
private: // [!] ?
    int *a;
public:
    A(const A& other) : a{new int[...]}
    {
        std::copy(...)
    }
}

class B : public A
{
private:
    int *b;
public:
    B(const B& other) : b{new int [...]}
    {
        std::copy(...)
    }
}

These are non-trivial classes, obviously. We need to deep copy the data in the copy constructor.

An example usage

int main()
{
    B binst;
    B binst2(binst); // [LABEL 1]
}

How does one write a copy constructor for class B, so that the value of int *a will be copied correctly at [LABEL 1]?

Someone else mentioned implicit type conversion between classes. But I don't yet know what that precisely means. (HOW will the implicit conversion be done.)

I'm sure this is an easy question, but I couldn't seem to find the answer.

Upvotes: 2

Views: 319

Answers (1)

M.M
M.M

Reputation: 141648

In this design, the copy constructor would be:

B(const B& other) : A(other), b{new int [...]}

You invoke A's copy-constructor to initialize the A member of this B object.

Note: A better design is to have each resource-managing object be self-contained. For example, if B actually had two pointer members initialized as b{new int}, c{new int} then your code cannot recover from an exception being thrown by the second new.

For example:

struct IntManager
{
    int *i;
    // here put your copy-constructors etc. etc.
};

struct A
{
    IntManager a;
};

struct B : A
{
    IntManager b;
};

This is called Rule of Zero. You avoid having to write any of the special functions for A or B (with the exception of constructors that are not copy/move constructors). So your code remains simple and there is no way to get it wrong, and you do not have code duplication everywhere like you will in your A and B examples.

NB. std::unique_ptr<int> might serve the role you need your int * to do , if so then use that to avoid reinventing the wheel. When pointers have ownership semantics it is good to indicate that . Raw pointers should only ever be used to point to things that are owned by someone else (and even then, try to avoid it).

Upvotes: 4

Related Questions