好的,还有一些代码。
#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.
}
现在你可能会说,为什么不把第一个双端的指针放到第二个双端?虽然我可以,但我需要数据是独立的。基本上我希望能够从第一个双端队列中克隆项目,同时保留其身份并为其提供自己的数据。
现在是当前修改后的版本
#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.
}
答案 0 :(得分:10)
处理的常见模式是通过基类中的虚拟clone()
方法创建适当类型的新对象:
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
}
答案 1 :(得分:3)
您需要提供虚拟副本构造函数 - 通常这是一个名为 clone
的方法 - 在每个类中重写以返回正确的类型:
class A {
virtual A* clone() {
return new A();
}
};
class B : public A {
void A* clone() {
return new B();
}
};
这些方法当然可以是任意复杂的,以便复制整个州。
当然,这会泄漏相当多的内存。使用适当的智能指针而不是原始指针(例如std::shared_ptr
如果编译器支持它,否则为boost::shared_ptr
。
答案 2 :(得分:1)
你有new A(...)
路。被称为A
的复制构造函数(由编译器隐式创建。
您想要的是clone
方法。见here。它从优秀的C++ Coding Standards书中回顾了相应的项目。下面是最终解决方案的无耻副本,它还显示了NVI idiom的良好用法,以避免slicing问题。
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 ) {/* … */}
};
<强>更新强> 一点解释。克隆的基本思想与此处的其他优秀答案相同。
现在,通过克隆,您将面临切片对象的危险。例如,如果某个派生自A
的对象忘记实现自己的clone
方法,则对A* a = d->clone()
的调用将不会返回完整的D
对象(假设{{ 1}}是D
)
NVI习惯用于将公共接口与虚拟接口分开。因此,在此示例中,A
为clone
,但不是public
。它调用virtual
方法protected virtual
,它执行实际克隆,以及哪些派生对象也实现。由于拆分,doClone
方法可以验证克隆对象的类型是否与原始对象的类型匹配。
答案 3 :(得分:-1)
我认为您将类与对象混淆,即这些类的实例。
您的容器aclasses
将指针存储到现有对象。您可以使用相同的指针并在许多不同的容器中将其推送几次,这是不称为克隆。