【问题标题】:Use static_cast in a class hierarchy to enable visitor pattern在类层次结构中使用 static_cast 来启用访问者模式
【发布时间】:2020-05-27 18:10:40
【问题描述】:

想象以下场景。我从一个库类派生来启用访问者模式:

#include <iostream>
#include <vector>

struct MyClassA;
struct MyClassB;

struct MyVisitor{
    virtual void visit(MyClassA* c) { std::cout << "My Class A" << std::endl; }
    virtual void visit(MyClassB* c) { std::cout << "My Class B" << std::endl; }
};

struct LibClass {};

struct MyClass : public LibClass {
    virtual void Accept(MyVisitor& visitor) = 0;
};

struct MyClassA : public MyClass {
    virtual void Accept(MyVisitor& visitor) {
        visitor.visit(this);
    }
};
struct MyClassB : public MyClass {
    virtual void Accept(MyVisitor& visitor) {
        visitor.visit(this);
    }
};

// vector signature can't be changed
void foo(std::vector<LibClass*>& v) {
    MyVisitor visitor;

    for(auto libC : v) {
        auto myC = static_cast<MyClass*>(libC); // questionable line
        //auto myC = dynamic_cast<MyClass*>(libC); // don't want to use dynamic cast
        myC->Accept(visitor);
    }
}

int main() {
    // vector signature can't be changed
    std::vector<LibClass*> v;    
    v.push_back(new MyClassA()); 
    v.push_back(new MyClassB()); 
    foo(v);
    return 0;
}

输出如预期:

My Class A
My Class B

这是一个关于设计的问题。在我看来,使用dynamic_cast 是不好的风格,应该避免使用。由于向量可能会变得很大,我还想避免在迭代中调用dynamic_cast
我可以肯定,向量中的每个指针都派生自MyClass
我想使用访问者模式来实现依赖于派生类的特性,所以我也会实现MyVisitor的派生类。 MyVisitor 以后可以为所有可能的孩子提供一个虚拟接口。具体的访问者只能覆盖他们想要与之交互的孩子的方法。

所以我的问题是:

  • 这样使用static_cast有什么危险吗?
  • 在我想插入使用访问者模式的可能性的情况下,您认为这是一个好的设计吗?

【问题讨论】:

  • “在我看来,使用dynamic_cast 是不好的风格,应该避免使用。” ——你还没有解释为什么你认为(有原因,但其中一些同样适用于static_cast)。
  • @KonradRudolph 在我的代码中这将是不好的风格,因为我会误用 dynamic_cast 作为开关。 (因为如果我使用 dynamic_cast,我根本不需要访问者)我想使用访问者来区分类型而不是 RTTI(或 dynamic_cast)

标签: c++


【解决方案1】:

dynamic_cast 更安全(假设检查 nullptr),因为编译器可能会在运行时检查该类是否真的是您想要的。 (由于可能的多重继承,没有编译时层次结构检查)。 foo 可以安全使用(即使LibClass 不是MyClass)。

使用static_cast,您向编译器做出承诺。打破它会导致UB。 在编译时完成的唯一检查是派生类 (MyClass) 实际上位于基类 (LibClass) 的层次结构中。
foo 签名现在具有误导性,正如您所期望的那样,只有 MyClass任何LibClass.
但只要你不违背承诺,代码就可以了。

【讨论】:

  • @RoQuOTriX:它基本上相当于一个虚拟通话,你也可以为访客做......
  • dynamic_cast 有轻微的运行时开销,无论如何这在大多数情况下都很好。 dynamic_cast 更安全,因为如果对象不是您期望的类型,它会返回 nullptr。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-25
  • 2012-10-02
  • 1970-01-01
相关资源
最近更新 更多