1

I have a class B which has a member that is a pointer to an object of a A class. When using copy constructor on an object of type A, it is copied but the member variable is not. Is there any way to copy an A object and to automatically make a copy of its B member? The following code shows the problem I'm triying to explain:

class A
{
public:
    A(char t_name)
    {
        name = t_name;
    }
    ~A()
    {
    }
    char name;
};

class B
{
public:
    A* attribute;

    B()
    {
        attribute = new A('1');
    }
    ~B()
    {}
};


int main()
{
    B* b_variable = new B;
    B* b_copy = new B(*b_variable);
    return 0;
}
Alex
  • 615
  • 2
  • 7
  • 15
  • Write the copy constructor, operator= and fix your destructor of class B. See posts on the rule of 3 and/or 5. http://stackoverflow.com/questions/4172722/what-is-the-rule-of-three http://stackoverflow.com/questions/4782757/rule-of-three-becomes-rule-of-five-with-c11 – Richard Critten Jun 23 '16 at 19:28
  • 1
    Why does your code reverse the meaning of `A` and `B` in your *description*? It's `B` that has a member pointing to an `A`. – Nicol Bolas Jun 23 '16 at 19:29
  • 1
    @Alex Why would you keep a pointer to something in this day and age anyway? Why not just keep an `A` member and then all your problems are solved. – Jonathan Mee Jun 23 '16 at 19:30
  • @NicolBolas true fact. I'm editing right now. – Alex Jun 23 '16 at 19:30
  • 1
    @JonathanMee Yes, it could be an option, but I want to know how to deal with this. Anyways I will reconsider your option too :) – Alex Jun 23 '16 at 19:34

3 Answers3

1

When using copy constructor on an object of type A, it is copied but the member variable is not.

Your code never calls any copy constructor in class A.

Your code calls a copy constructor in class B and it does exactly what is is supposed to, i.e. copies the value of attribute which is a pointer to a class A object.

In other words - after executing your code, you have two instances of class B and one class A instance. In the two class B instances attribute points to the same class A instance.

This is (most likely) not what you want.

As many already has pointed out (e.g. see @lostbard answer), you'll need a copy constructor in class B to do a deep-copy. A deep-copy is needed because class B have a pointer member.

Also you should do some clean up in class B destructor and in main.

#include <iostream>
using namespace std;

class A
{
public:
    A(char t_name)
    {
        name = t_name;
    }

    ~A()
    {
    }
    char name;
};

class B
{
public:
    A* attribute;

    B()
    {
        attribute = new A('1');
    }

    /** Copy constructor */
    B(const B &copy)
    {
        // Make a new instance of class A
        attribute = new A(*copy.attribute);
    }

    /** Assignment operator */
    B& operator= (const B& other)
    {
        // Delete the existing class A instance
        delete attribute;
        // and create a new as a copy of other.attribute
        attribute = new A(*other.attribute);
    }

    ~B()
    {
        // Delete the class A instance
        delete attribute;
    }
};


int main()
{
    B* b_variable = new B;
    B* b_copy = new B(*b_variable);

    // Delete the two class B instances
    delete b_variable;
    delete b_copy;

    return 0;
}

There is no need for a copy constructor in class A. The default generated will do as Class A has no pointer members.

EDIT

As pointed out by @Slava you should always implement a assignment operator when you make a copy constructor (rule of three) so I added it to the code above.

Some like the rule of three to be the rule of five so it also include move. Read more here: https://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming)

Support Ukraine
  • 42,271
  • 4
  • 38
  • 63
0

While there are many solutions, I'd bet sooner-or-later you'll end up with implementing the usual virtual a.clone();. That works flawless when you'll have derived classes of A (which is more-or-less the only legitimate reason why you're keeping A as a pointer to a heap-allocated object and not as a value member :) ).

Note that, when you're implementing clone() in your hierarchy, that C++ supports covariant pointer return types. Thus, if base has a virtual that returns e.g. Clonable*, then A's same method can return A* and A's descendant ADerived can return ADerived*. Feel free to understand 'can' as 'should' for the case of clone().

lorro
  • 10,687
  • 23
  • 36
  • Think you're headed down the wrong path here. If this was a question of inheritance, then `clone` may be useful. But since this is a contains relationship the copy constructor is the better option by far. `clone` does not make a class Rule of Three compliant. – user4581301 Jun 23 '16 at 19:43
  • I'm not saying right now OP needs it, I'm saying sooner-or-later we'll end up with it. Otherwise why wouldn't we simply have `A attribute;` instead of `A*`? The class allocates A itself, so pool of A objects is probably not the reason. As for Rule of Three, I'm not compliant, either: I tend to make const correct classes, where assignment has no valid meaning. Perhaps we shouldn't take that rule that strictly, rather take it as a hint :). – lorro Jun 23 '16 at 20:39
0

Create a copy constructor for A and for B:

class A
{
public:
    A(char t_name)
    {   
    name = t_name;
    }
    A(const A& copy)
    {   
        name = copy.name;
    }
    ~A()
    {
    }
    char name;
};

class B
{
public:
    A* attribute;

    B()
    {
        attribute = new A('1');
    }
    B(const B &copy)
    {
      attribute = new A(*copy.attribute);
    }
    ~B()
    {}
};
lostbard
  • 5,065
  • 1
  • 15
  • 17
  • 1
    It is not necessary to create copy ctor for `A` – Slava Jun 23 '16 at 19:37
  • it is not required in this case, since the field is just a char, but if it was a more complex problem, it might become required. – lostbard Jun 23 '16 at 19:39
  • It is so nice to care about hypothetical case, especially when you did not care about memory leak in dtor and missing assignment operator. – Slava Jun 23 '16 at 20:05