基于dynamic_cast与虚拟AsDerived方法

时间:2016-08-23 08:17:30

标签: c++ inheritance casting

当尝试访问派生类行为时,我读到的最常见的方法是使用dynamic_cast s,即dynamic_cast<DerivedA*>(BasePtr)->DerivedAOnlyMethod()。这并不是非常漂亮,但是每个人都明白发生了什么。

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

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,这使基类变得混乱,并且暴露了它不应该需要的派生类的知识。

我太缺乏经验,无法确定哪个更好,所以我正在寻找支持和反对这两种结构的论据。哪个更惯用?他们如何比较性能和安全性?

4 个答案:

答案 0 :(得分: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 :(得分:2)

好吧,将AsDerived@ - 方法放入基类肯定会导致更快的转换。

如果使用final限制继承层次结构,则可以减少或删除优势。

另外,你说它是不常见的,因为它引入了混乱,它将所有相关派生类的知识引入基类。

总之,可能有时会在瓶颈中有用,但为这种可憎的行为付出代价。

答案 2 :(得分: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的非中庸是至关重要的。

答案 3 :(得分:1)

这样的解决方案不仅会使基类变得混乱,而且会对它产生不必要的依赖,这是完全正确的。在一个干净的设计中,基类不需要并且实际上不应该知道关于其派生类的任何。其他一切很快就会成为维护的噩梦。

但是,我想指出我在&#34;尽量避免dynamic_cast&#34; -Team。这意味着我经常看到dynamic_cast可以通过适当的设计避免。因此,首先要问的问题是:为什么我需要知道派生类型?通常有一种方法可以通过正确使用多态来解决问题,或者&#34;失去&#34;类型信息已经是错误的路径。

更喜欢使用多态而不是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.