当尝试访问派生类行为时,我读到的最常见的方法是使用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,这使基类变得混乱,并且暴露了它不应该需要的派生类的知识。
我太缺乏经验,无法确定哪个更好,所以我正在寻找支持和反对这两种结构的论据。哪个更惯用?他们如何比较性能和安全性?
答案 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.