2015-09-09 40 views
2

这已经是我发送的关于这个子喷墨机的几篇文章了,如果这让你们有些困扰,我很抱歉。在玩了几天后,尝试了不同的选择(模式)后,我得出了以下结论,这使得我可以更好地制定我的问题。C++多态克隆:如何从基础指针获取派生指针?

我有一个向量Base *,并有从基地派生的各种Derived<T>类。不同的类参数T在前面声明为前向声明。我需要的是从这个vector<Base *>中获取一对对象,并使用模板来应用function(Derived<T1> obj1, Derived<T2> obj2)以进行某种双重调度。然而,当我从vector中取出一个元素时,我无法将pointer-to-base转换为Derived指针。因此,我不能使用function(Derived<T1> obj1, Derived<T2> obj2)

当然,这可以简单地使用dynamic_cast<Derived<T> *>(base_ptr)来实现。但是,这并不是我想要的,因为要做到这一点,必须事先知道T,而这里并不是这种情况。我必须能够从矢量中选择任何元素,而不必知道它们的类型。

因此,我尝试了多态克隆。尽管它使用协变返回类型,但不幸的是,它不起作用。正如SO中的不同人员以及C++标准所解释的,原因是在编译时,编译器仍然不知道对象的完整类型。因此,虽然有人希望clone()应该返回Derived<T> *,但它会返回Base *

下面几行代码表达了我的意思。 一般而言,多态克隆在下列情况下使用:

Base *b1 = new Derived; 
Base *b2; 
b2 = b1->clone(); 

这不是我想要的。我需要的是一个指针到派生:

Base *b1 = new Derived; 
Derived *d1; 
d1 = b1->clone(); // This is what I want, but it fails. 

这里,编译器会抱怨说,Base *类型的对象不能被分配到Derived *类型的对象。因此,我不能在function(,)中使用*d1

我也尝试了非虚拟接口(NVI)方法,但它没有奏效。

有没有人有一个想法如何解决上面的简单代码,特别是最后一行?提前谢谢了。

+2

查看[访问者模式](https://en.wikipedia.org/wiki/Visitor_pattern)多次发送。 – TartanLlama

回答

0

我想在这种情况下,你应该做铸造。你可以谷歌更多关于static_Cast和reinterpret_cast。下面是一些参考:How to force implicit conversion in polymorphismProper way of casting pointer types

+0

问题可以通过dynamic_cast来解决,的确如此。但这不是我想要的,因为我必须事先知道类型(派生)来做'dynamic_cast *>',正如我在文章中所解释的那样。如果我从我的'矢量'中随机抽取两个元素,先验他们将是'派生的'类型和派生的',我不知道。因此,如果我做了dynamic_cast,那么我将不得不做一些if-else来检查转换是否正常,这是我想避免的。 –

3

你想要什么是不可能的,因为没有保证的对象指向一个Base *实际上是Derived一个实例。

您可以做的最多的是对clone()函数使用协变返回类型。

class Base 
{ 
public: 
    virtual Base* clone() const 
    { 
     return new Base(*this); 
    }; 
    virtual ~Base() {}; 
}; 

class Derived : public Base 
{ 
public: 
    virtual Derived* clone() const // note different return type 
             // this does override the inherited version 
    { 
     return new Derived(*this); 
    } 
}; 


int main() 
{ 
    Base *b = new Derived; 
    Derived *d1 = b->clone(); // this does not work 

    Derived *o = new Derived; 
    Derived *d = o->clone(); // this works 
} 

如果你想从一个Base *克隆到Derived *,有必要做一个运行时检查和强制类型转换。

Base *b = new Derived; 

Base *temp = b->clone(); 
Derived *d = dynamic_cast<Derived *>(temp); 
if (d) 
{ 
    // d is actually an instance of Derived so can be used as such 
} 
else 
{ 
    // The cloning did not produce a Derived. Cope with it 
} 
delete temp; // this will delete the object pointed to by d, since it is the same object 

但是,一般来说,你所要求的是一个有缺陷的设计的积极指标。一般来说(假设你需要一个具有多态基的类层次结构),你需要设计Base,这样它的接口(特别是一组虚拟函数,可以被派生类专用)足以让该类的所有用户都能够执行他们需要。 Base的所有用户应该需要的是指针或对Base(即Base &Base *)的引用,并且应该不需要它们从Base投射到Derived。查阅“Liskov替代原则”了解更多信息。

+0

感谢您的回答。事实上dynamic_cast可以用两行解决问题,甚至不需要使用clone()。然而,我有不同的'派生',如果我使用dynamic_cast,我将不得不使用一些if-else,就像你在代码中所做的那样,我想避免这种情况。此外,类别参数T不是预先知道的,它们在用户开始时由前向声明来定义。因此,每次定义不同的T时,必须更改代码(if-else)。我正在寻找一些自动化方法,如clone()。 –

+0

@Peter如果你要通过'Base'指针删除一个派生对象,那么Base的析构函数必须是虚拟的。 – user2079303

+0

@ ChristopheJ.Ortiz如果您在运行时不知道“Base *”指向“Derived ”,但想获得“Derived *”,那么根本无法避免if-else检查。在C++中有一个构造,可以在编译时轻松解决不断变化的类参数,而无需更改代码。这是一个模板。 – user2079303

相关问题