简体   繁体   English

抽象基类:如何为包含指向(抽象)基类的指针的类定义复制构造函数或赋值运算符?

[英]Abstract Base Classes: How do you define a copy constructor or assignment operator for a class that contains a pointer to a (abstract) base class?

I just encountered with question on parashift.com regarding Abstract base classes in c++ . 我刚刚在parashift.com上遇到过关于c ++中抽象基类的问题。

Author has provided the solution in which pure virtual member function Clone() has been created in Abstract Base Class.The purpose of this function is to create and return the address of clone object of ABC pointing to. 作者提供了在Abstract Base Class中创建纯虚拟成员函数Clone()的解决方案。该函数的目的是创建并返回ABC指向的克隆对象的地址。 Here I am bit confused that what is the use of creating this virtual function and overriding the Assignment operator and copy constructor if we achieve the same thing without doing this. 在这里我有点困惑的是,如果我们在没有这样做的情况下实现相同的事情,那么创建这个虚函数和覆盖Assignment操作符和复制构造函数的用途是什么。

class Shape {

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

Then we implement this clone() method in each derived class. 然后我们在每个派生类中实现这个clone()方法。 Here is the code for derived class Circle: 以下是派生类Circle的代码:

class Circle : public Shape {

public:

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

};

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

Now suppose that each Fred object “has-a” Shape object. 现在假设每个Fred对象“都有一个”Shape对象。 Naturally the Fred object doesn't know whether the Shape is Circle or a Square or … Fred's copy constructor and assignment operator will invoke Shape's clone() method to copy the object: 当然,Fred对象不知道Shape是Circle还是Square还是...... Fred的复制构造函数和赋值运算符将调用Shape的clone()方法来复制对象:

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 above behavior without overriding the Assignment operator or copy constructor. 我认为我们可以在不重写Assignment操作符或复制构造函数的情况下实现上述行为。 If we have two object f1 (P_ pointing to Circle) and f2 (P_ pointing to Square) of type Fred. 如果我们有两个对象f1 (P_指向Circle)和f2 (P_指向Square)类型为Fred。 then 然后

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

In the default case , P_ (Address of Square) of f2 will get copied to P_ to f1 . 在默认情况下, f2 P_ (Square的地址)将被复制到P_f1 now f1 will point to Square. 现在f1将指向Square。 Only thing we need to take care is to delete the object of Circle otherwise it will be dangling state. 我们唯一需要注意的是删除Circle的对象,否则它将是悬空状态。

Why has the author mentioned the above technique to solve this problem? 为什么作者提到了上述技术来解决这个问题? Please Advise. 请指教。

It is true that 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 – is required to know that he has to call delete f1.p first. 但这意味着Fred类的用户 - 不一定是它的作者 - 需要知道他必须先调用delete f1.p It might be obvious to you now, but other people would be very surprised that a simple assignment causes memory leaks. 现在对你来说可能很明显,但是其他人会因为一个简单的赋值导致内存泄漏而感到非常惊讶。 Also, if you return to your code after a long time, maybe you forget this little rule yourself and do the mistake. 此外,如果你在很长一段时间后回到你的代码,也许你自己忘记了这个小规则,并做错了。

Since you always have to delete the shape before assigning Fred, it is absolutely sensible to write this in the overriden equals operator. 由于在分配Fred之前总是必须删除形状,因此在overriden equals运算符中写入它是绝对明智的。 So the deletion happens automatically and the user does not need to worry about it. 因此删除会自动发生,用户无需担心。

EDIT to answer question in comment : 编辑回答评论中的问题

The virtual Shape *clone() function in the base class enforces every derived class to implement the clone() function. 基类中的virtual Shape *clone()函数强制每个派生类实现clone()函数。 If you derive from Shape and forget to implement clone() , your code will not compile. 如果从派生派生并忘记实现clone() ,则代码将无法编译。 This is good because the overriden assignment operator of Fred relies on it. 这很好,因为Fred的重写赋值运算符依赖于它。

In this case you want to make a deep copy of your Fred object. 在这种情况下,您需要制作Fred对象的深层副本。 Since the destructor does a delete p_; 由于析构函数执行了delete p_; , if you had two Fred objects pointing at the same Shape , you would get a double free error. ,如果你有两个指向同一个Shape Fred对象,你会得到一个双重自由错误。 The reason for the clone() interface is that Fred doesn't know what type of object p_ points at, so it can't directly call the correct copy constructor. clone()接口的原因是Fred不知道p_指向什么类型的对象,因此它无法直接调用正确的复制构造函数。 Instead it relies on Shape 's subclasses to create copies of themselves and uses virtual method dispatch to create the right type of object. 相反,它依赖于Shape的子类来创建自己的副本,并使用虚方法调度来创建正确类型的对象。

The example is not trying to make an assignment of f2 to f1, so the OP's belief that f1 = f2; 这个例子并没有试图将f2赋值给f1,所以OP认为f1 = f2; exhibits the same behaviour is incorrect. 表现出相同的行为是不正确的。 The example is assigning a copy of f2 to f1, so the behaviour is closer to f1 = new Whatever_f2_Is(*f2) . 示例是将f2的副本分配给f1,因此行为更接近于f1 = new Whatever_f2_Is(*f2)

Since f2 is a pointer to a base class, there isn't enough information at this point to know which copy constructor to use (a slight lie, but the clone method is still easier to use). 由于f2是指向基类的指针,因此此时没有足够的信息来知道要使用哪个复制构造函数(稍有​​说谎,但克隆方法仍然更容易使用)。 You cannot call new Shape() even if shape wasn't pure virtual and thus uninstantiatable because shape does not know the extra information of the sublclass. 你不能调用new Shape(),即使shape不是纯虚拟的,因此也是不可实例化的,因为shape不知道子类的额外信息。 You would have a Shape, but lose all of the extra aspects of Circle-ness or Square-ness. 你会有一个Shape,但会失去Circle-ness或Square-ness的所有额外方面。

Fortunately while we know nothing about what is really at f2, the object at f2 still knows what it is, and we can delegate the creation of the copy to it. 幸运的是,虽然我们对f2的实际情况一无所知,但f2上的对象仍然知道它是什么,我们可以将副本的创建委托给它。 That's what the clone method does for you. 这就是克隆方法为您所做的。

The alternative is to play is-a games or use a ID code and a factory. 另一种方法是玩游戏或使用ID代码和工厂。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 我可以在抽象基类中定义构造函数吗? - Can I define a constructor in an abstract base class? 定义抽象基类的默认构造函数 - Define default constructor for abstract base class 如何通过继承实现移动构造函数和移动赋值运算符(抽象基类) - How to implement move constructor and move assignment operator with inheritance(abstract base class) 如何实现抽象基类的子类构造函数? - How to implement subclass constructor of abstract base class? 在抽象基类上实现operator &lt; - Implementing operator< on abstract base class 指向抽象模板基类的指针? - A pointer to abstract template base class? 类的赋值运算符和复制构造函数,其中包含指向派生模板化类的基类指针 - Assignment operator and copy constructor for class containing base class pointer to derived templated class 如何初始化抽象基类的受保护成员? - How do you initialize protected members of an abstract base class? 重载了数据成员,构造函数和运算符的抽象基类 - Abstract base class with data member, constructor and operator overloaded 我可以使用指针而不是引用(&)为Abstract基类编写复制构造函数吗? - Can I write a copy constructor for an Abstract base class using pointer instead of reference (&)?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM