Abstract base classes. How do I define a copy constructor or assignment operator for a class that contains a pointer to an (abstract) base class?

I just ran into a question on parashift.com regarding abstract base classes in C ++.

The author provided a solution in which a pure virtual member function was created in an abstract base class Clone()

. The purpose of this function is to create and return the address of the ABC clone object pointing to. Here I am a bit confused about what is used to create this virtual function and override the Assignment statement and copy constructor if we achieve the same without doing so.

class Shape {

    public:
      // ...
      virtual Shape* clone() const = 0;   // The Virtual (Copy) Constructor
      // ...
};

      

We will then implement this method Clone()

in each derived class. Here is the code for the derived class Circle:

class Circle : public Shape {

public:

  // ...
  virtual Circle* clone() const;
  // ...

};

Circle* Circle::clone() const
{
  return new Circle(*this);
}

      

Now, suppose each Fred object has a "aa" Shape object. Naturally, the Fred object doesn't know if the Shape will be a Circle or Square or ... Freds copy constructor and the assignment operator will call the Shapes method Clone()

to copy the object:

class Fred {
public:
  // p must be a pointer returned by new; it must not be NULL
  Fred(Shape* p)
    : p_(p) { assert(p != NULL); }
 ~Fred()
    { delete p_; }
  Fred(const Fred& f)
    : p_(f.p_->clone()) { }
  Fred& operator= (const Fred& f)
    {
      if (this != &f) {              // Check for self-assignment
        Shape* p2 = f.p_->clone();   // Create the new one FIRST...
        delete p_;                   // ...THEN delete the old one
        p_ = p2;
      }
      return *this;
    }
  // ...
private:
  Shape* p_;
};

      

As I think we can achieve the above behavior without overriding the Assignment statement or the copy constructor. If we have two objects f1

(P_ pointing to Circle) and f2

(P_ pointing to square) of type Fred. then

f1=f2;  // This line exhibits the same behavior what  above code is doing. 

      

In the default case P_

(area address) f2

will be copied to P_

before f1

. f1

will now point to a square. The only thing we need to care about is deleting the Circle object, otherwise it will be dangling.

Why did the author mention the above technique for solving this problem? Please advise.

+3


source to share


3 answers


It's true what you can do

delete f1.p;
f1 = f2;

      

But this means that the user of the Fred class, who is not necessarily the author of it, needs to know what he should call first delete f1.p

. This may be obvious now, but other people would be very surprised that a simple assignment causes a memory leak. Also, if you come back to your code after a long time, you may forget this little rule yourself and make a mistake.



Since you must always remove the form before assigning Fred, it makes perfect sense to write this in the overriden equals statement. This way, the deletion happens automatically and the user doesn't need to worry about it.

EDIT to answer the question in the comment :

A function virtual Shape *clone()

in the base class enforces each derived class to implement the clone () function. If you derive from Shape and forget to implement clone()

, your code won't compile. This is good because the Fred override operator relies on it.

+1


source


In this case, you want to make a deep copy of your object Fred

. Since the destructor does delete p_;

, if you have two objects Fred

pointing to the same Shape

, you will get a double loose error. The reason for the interface clone()

is that it Fred

doesn't know what type of object it p_

points to, so it cannot directly call the correct copy constructor. Instead, it relies on subclasses Shape

to create the copies themselves, and uses the virtual method manager to create the desired object type.



+1


source


The example does not attempt to assign f2 to f1, so the OP's statement that it f1 = f2;

exhibits the same behavior is incorrect. The example assigns a copy of f2 to f1, so the behavior is closer to f1 = new Whatever_f2_Is(*f2)

.

Since f2 is a pointer to the base class, there is not enough information at this time to know which copy constructor to use (small lie, but the clone method is still easier to use). You cannot call the new Shape () even if the shape was not pure virtual and thus incurable, because the form does not know any additional information about the sublclass. You will have Shape, but you will lose all additional aspects of Circle-ness or Square-ness.

Fortunately, while we don't know anything about what really is in f2, the object in f2 still knows what it is, and we can delegate making a copy to it. This is what the clone method does for you.

An alternative is playing games or using an ID code and factory.

0


source







All Articles