【问题标题】:Is using RTTI (or virtual functions that return a type tag) ever OK?使用 RTTI(或返回类型标签的虚函数)是否可以?
【发布时间】:2019-06-06 23:54:26
【问题描述】:

使用 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 的指针时,它可能是 Derived1Derived2

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/虚函数和类型标签可以吗?

【问题讨论】:

  • 即使是令人恐惧和鄙视的goto也有很好的用途。
  • 对不起,为什么 RTTI 被认为很差?你还有什么?好的编程是在正确的地方使用正确的东西,而不是仅仅因为你被告知使用或不使用某物而使用或不使用某物。
  • 正确的问题通常不是“这种方法是好是坏”,而是“有没有更好的方法来完成我的任务?”在这种情况下,更好的方法通常是虚拟方法。
  • 在这个例子中,我认为最好将解析器更改为明确地只允许赋值左侧的变量,但总的来说我同意R Sahu's answer

标签: c++ rtti


【解决方案1】:

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

当我看到这样的代码时,我会使用 Open-Closed Principle 作为指导。

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-03-05
    • 2018-12-08
    • 2014-12-22
    • 1970-01-01
    • 2021-08-30
    • 1970-01-01
    • 2020-02-28
    相关资源
    最近更新 更多