简体   繁体   English

复制 c++ 抽象类

[英]copying c++ abstract classes

Ok heres some code.好的,这里有一些代码。

#include <iostream>
#include <deque>
using namespace std;
class A
{
public:
    virtual void Execute()
    {
        cout << "Hello from class A" << endl;
    }
};
class B: public A
{
public:
    void Execute()
    {
        cout << "Hello from class B" << endl;
    }
};
void Main()
{
    deque<A *> aclasses = deque<A*>(0);
    deque<A *> aclasses2 = deque<A*>(0);
    A a1 = A();
    B b1 = B();
    aclasses.push_back(&a1);
    aclasses.push_back(&b1);
    aclasses[0]->Execute();
    aclasses[1]->Execute();

    //Now say I want to copy a class from aclasses to aclasses2
    //while perserving it's identity and making it a seperate entity, without
    //knowing the exact type it is.

    aclasses2.push_back(new A(*aclasses[0]));
    aclasses2.push_back(new A(*aclasses[1]));
    //Now my problem show itself
    for each(A * a in aclasses2)
        a->Execute();
    //Execute is called from the original class A both times.

}

Now you might say, why don't you just put the pointers from the first deque into the second deque?现在你可能会说,为什么不把第一个双端队列的指针放到第二个双端队列中呢? While I could but I need the data to be independent.虽然我可以,但我需要数据是独立的。 Basically I want to be able to clone items from the first deque while preserving there identity and giving them there own data.基本上我希望能够从第一个双端队列中克隆项目,同时保留那里的身份并给他们自己的数据。

Now the current modified version现在是当前修改版本

#include <iostream>
#include <deque>
using namespace std;
class A
{
public:
    virtual void Execute()
    {
        cout << "Hello from class A" << endl;
    }
    virtual ~A() {}             // don't forget the virtual destructor
    virtual A* clone() const {
       return new A(*this);
    }
};
class B: public A
{
public:
    void Execute()
    {
        cout << "Hello from class B" << endl;
    }
    virtual B* clone() {     // return type is co-variant
       return new B( *this );
    }
};
void MainRUNNER()
{
    deque<A *> aclasses = deque<A*>(0);
    deque<A *> aclasses2 = deque<A*>(0);
    A a1 = A();
    B b1 = B();
    aclasses.push_back(&a1);
    aclasses.push_back(&b1);
    aclasses[0]->Execute();
    aclasses[1]->Execute();

    //Now say I want to copy a class from aclasses to aclasses2
    //while perserving it's identity and making it a seperate entity, without
    //knowing the exact type it is.

    aclasses2.push_back(aclasses[0]->clone());
    aclasses2.push_back(aclasses[1]->clone());
    //Now my problem show itself
    for each(A * a in aclasses2)
        a->Execute();
    //Execute is called from the original class A both times.
}

The common pattern for handling that is through a virtual clone() method in the base class that will create a new object of the appropriate type:处理的常见模式是通过基础 class 中的虚拟clone()方法,该方法将创建适当类型的新 object:

struct base {
    virtual ~base() {}             // don't forget the virtual destructor
    virtual base* clone() const { 
       return new base(*this); 
    }
};
struct derived : base {
    virtual derived* clone() const {     // return type is co-variant
       return new derived( *this );
    }
};
int main() {
   std::auto_ptr<base> b1( new derived );
   std::auto_ptr<base> b2( b1->clone() ); // will create a derived object
}

You need to provide a virtual copy constructor – usually this is a method called clone – which is overridden in each class to return the correct type:您需要提供一个虚拟复制构造函数——通常这是一个称为clone的方法——它在每个 class 中被覆盖以返回正确的类型:

class A {
    virtual A* clone() {
        return new A();
    }
};

class B : public A {
    void A* clone() {
        return new B();
    }
};

The methods can of course be arbitrarily complex in order to copy the whole state.为了复制整个 state,这些方法当然可以任意复杂。

Of course, this leaks rather a lot of memory.当然,这泄露了相当多的 memory。 Use appropriate smart pointers instead of raw pointers (eg std::shared_ptr if your compiler supports it, boost::shared_ptr otherwise).使用适当的智能指针而不是原始指针(例如,如果您的编译器支持std::shared_ptr ,则使用boost::shared_ptr否则)。

You have new A(...) way down there.你在那里有new A(...)方式。 What gets called is A 's copy constructor (created implicitly by the compiler.调用的是A的复制构造函数(由编译器隐式创建。

What you want is a clone method.你想要的是一个clone方法。 See here .这里 It recaps the appropriate item from the excellent C++ Coding Standards book.它概括了优秀的C++ 编码标准书中的适当项目。 Below is a shameless copy of the final solution, which also shows a nice use of the NVI idiom to avoid the slicing problem.下面是最终解决方案的无耻副本,它还展示了一个很好的使用NVI 成语来避免切片问题的方法。

class A {// …
public:
  A* Clone() const {                        // nonvirtual
    A* p = DoClone();
    assert( typeid(*p) == typeid(*this) && "DoClone incorrectly overridden" );
    return p;                                // check DoClone's returned type
  }

protected:
 A( const A& );
 virtual A* DoClone() const = 0;
};

class B : public A { // …
public:
  virtual B* Clone() const {return new B(*this); }

protected:
  B( const B& rhs ) : A( rhs ) {/* … */}
};

update A bit of an explanation.更新一点解释。 The basic idea of the clone is the same as the other excellent answers here.克隆的基本思想与这里的其他优秀答案相同。

Now, with cloning you have the danger of slicing objects.现在,通过克隆,您有切割对象的危险。 For example, if some object which derives from A forgets to implement its own clone method, then a call to A* a = d->clone() will not return a full D object (assuming D is a descendant of A )例如,如果从A派生的某些 object 忘记实现自己的clone方法,则对A* a = d->clone()的调用将不会返回完整的D object (假设DA的后代)

The NVI idiom says to separate a public interface from a virtual interface. NVI 习语说将公共接口与虚拟接口分开。 Thus, in this example, clone is public , but not virtual .因此,在此示例中, clonepublic ,但不是virtual It call a protected virtual method, doClone , which does the actual cloning, and which derived objects also implement.它调用一个protected virtual方法doClone ,该方法执行实际的克隆,并且派生对象也实现了该方法。 Because of the split, the clone method can verify that the type of the cloned object matches the type of the original object.由于拆分, clone方法可以验证克隆的 object 的类型是否与原始 object 的类型匹配。

I think you confuse classes with objects , ie instances of those classes.我认为您将对象(即这些类的实例)混淆了。

Your container aclasses store pointers to existing objects.您的容器aclasses存储指向现有对象的指针。 You may take the same pointer and push it several times in many different containers, this is not called cloning.您可以将同一个指针在许多不同的容器中多次推送,这称为克隆。

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM