2016-08-23 58 views
2

当试图访问派生类行为时,我读到的最常见方法是使用dynamic_cast s,即dynamic_cast<DerivedA*>(BasePtr)->DerivedAOnlyMethod()。这不太好,但每个人都明白发生了什么。dynamic_cast vs基于虚拟AsDerived方法

现在我正在此转换由导出基类,对于每一个派生类的虚函数处理的代码,即:

class Base 
{ 
public: 
    virtual DerivedA* AsDerivedA() { throw Exception("Not an A"); } 
    virtual DerivedB* AsDerivedB() { throw Exception("Not a B"); } 
    // etc. 
}; 
class DerivedA : public Base 
{ 
public: 
    DerivedA* AsDerivedA() { return this; } 
}; 
// etc. 

使用是那么BasePtr->AsDerivedA()->DerivedAOnlyMethod()。 Imho,这使基类变得混乱,并暴露出它不应该需要的派生类的知识。

我太缺乏经验,肯定地说哪个更好,所以我正在寻找争论和反对任何构造。哪一种比较习惯?他们如何比较性能和安全性?

+3

这些函数打破[open closed principle] [1]。如果您要添加C类,则需要更改基类以添加AsDerivedC。 [1]:https://en.wikipedia.org/wiki/Open/closed_principle – Davidbrcz

回答

2

好,把[email protected] - 方法为基础类肯定会导致潜在更快的铸造。

如果使用final来限制继承层次结构,那么优点可能会减少或删除。

此外,你是正确的,因为它引入了混乱,它引入了所有相关的派生类到基类的知识是不常见的。

总之,可能有时是有用的一个瓶颈,但你为可憎工资。

1

你的直觉是对的,AsDerivedX方法是混乱的。事实上,在运行时可以检查这些虚拟函数是否过载相当于类型检查的代价。所以,在我看来,这样的C++的方法是:

void doSomething(Base *unsureWhichAorB) { 
    DerivedA *dA = dynamic_cast<DerivedA*>(unsureWhichAorB); 
    if(dA) //if the dynamic cast failed, then dA would be 0 
     dA->DerivedAOnlyMethod(); 
} 

注意,对于dA非zeroness的检查是关键在这里。

2

没有看到更多的代码,很难提供太多的建议。然而,需要知道你所调用的对象的类型更多地是针对变体而不是多态的类型。

多态性是关于信息隐藏。来电者不需要知道他拿着什么类型。

这样的事情,也许?

struct base 
{ 
    virtual bool can_do_x() const { return false; } 
    virtual void do_x() { throw std::runtime_error("can't"); } 
    virtual ~base() = default; 
}; 

struct derived_a : base 
{ 
    virtual bool can_do_x() const { return true; } 
    virtual void do_x() { std::cout << "no problem!"; } 
}; 

int main() 
{ 
    std::unique_ptr<base> p = std::make_unique<derived_a>(); 
    if (p->can_do_x()) { 
    p->do_x(); 
    } 
} 

现在我们正在讨论的是对象的能力而不是类型。

1

你完全正确的认为,这样的解决方案不仅使基类混乱,而且对其不必要的依赖。在一个干净的设计中,基类不需要,实际上不应该知道什么关于它的派生类。其他一切都将很快成为维护噩梦。

但是,我想指出我在“尽量避免dynamic_cast” - 团队。这意味着我经常看到dynamic_cast,这可以通过适当的设计来避免。所以首先要问的问题是:为什么我需要知道派生类型?通常要么通过正确使用多态性来解决问题,要么“丢失”已经是错误路径的类型信息。

喜欢使用多态,而不是dynamic_cast

class Base 
{ 
public: 
    virtual void doSomething() = 0; 
}; 

class DerivedA : public Base 
{ 
public: 
    void doSomething() override { //do something the DerivedA-way }; 
}; 

class DerivedB : public Base 
{ 
public: 
    void doSomething() override { //do something the DerivedB-way }; 
}; 
// etc. 
+0

如果所需函数在行为中是多态的,那么它们就是基类中的虚函数。但有时你需要派生类的具体方法。 – Psirus

+1

然后我会质疑决定将它们分配给'Base'类型的变量。如果你 - 在某种程度上 - 需要知道实际类型,你为什么“首先把它扔掉”? – sigy

+0

将它们存储在普通容器中。 – Psirus