使用 RTTI(或返回类型标记的虚函数)是否正常

Is using RTTI (or virtual functions that return a type tag) ever OK?

本文关键字:函数 是否 RTTI 返回类型 使用      更新时间:2023-10-16

使用RTTI(通过使用typeiddynamic_cast)几乎被普遍认为是糟糕的编程实践。

类似地,定义所有导数都必须通过虚函数返回的类型标签也被认为是不好的做法,例如:

enum Type {
    DERIVED_1,
    DERIVED_2
};
class Base {
    virtual Type type() = 0;
};
class Derived1 : public Base {
    Type type() override {
        return DERIVED_1;
    }
};
class Derived2 : public Base {
    Type type() override {
        return DERIVED_2;
    }
};

但是,有时我需要区分不同的派生类,例如当我有一个指向Base的指针时,该指针可能是Derived1的,也可能是Derived2的:

Base *b = new Derived2();
// Approach 1:
if (typeid(*b) == typeid(Derived1)) {
    std::cout << "I have a Derived1.n";
} else if (typeid(*b) == typeid(Derived2)) {
    std::cout << "I have a Derived2.n";
}
// Approach 2:
if (b->type() == DERIVED_1) {
    std::cout << "I have a Derived1.n";
} else if (b->type() == DERIVED_2) {
    std::cout << "I have a Derived2.n";
}

人们说拥有基于类型的决策树是一种不好的做法,但有时这是必要的!

假设我正在编写编译器,需要决定是否可以将给定的表达式分配给:

/* ... */
Expr* parseAssignment(Expr *left) {
    // Is "left" a type of Expr that we can assign to?
    if (typeid(*left) == typeid(VariableExpr)) {
        // A VariableExpr can be assigned to, so continue pasrsing the expression
        /* ... */
    } else {
        // Any other type of Expr cannot be assigned to, so throw an error
        throw Error{"Invalid assignment target."};
    }
}
(假设 Expr 是

基类,而 VariableExpr 是衍生物)

有没有其他方法可以实现这种不被认为是不良做法的行为?或者在这种情况下 RTTI/虚拟函数和类型标签可以吗?

使用dynamic_cast不仅可以,而且在许多情况下都是必不可少的。

当我看到这样的代码时,我使用开闭原则作为指导。

如果我必须在将新的派生类型添加到系统时重新访问该if-else块或enum,我认为这是一个问题。如果没有,我不认为这是一个问题。

当您看到级联if-else代码块时,它通常违反了开闭原则,应避免使用。避免这种情况的方法是使用回调机制。

  1. 让基类有一个函数来注册派生类型的回调函数。
  2. 在基类的业务逻辑中,检查是否已为派生类型注册函数。如果是,请调用该函数。否则,要么以静默方式忽略它,要么需要引发异常。