使用 RTTI(通过使用 typeid
和 dynamic_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
不仅可以使用,而且在许多情况下都是必不可少的。
当我看到这样的代码时,我会使用 Open-Closed Principle作为指南。
如果在系统中添加新的派生类型时我必须重新访问 if-else
block 或 enum
,我认为这是一个问题。如果不是,我认为这不是问题。
当您看到级联的if-else
代码块时,它通常违反了Open-Closed Principle。并且应该避免。避免这种情况的方法是使用回调机制。
- 让基类有一个函数,为派生类型注册一个回调函数。
- 在基类的业务逻辑中,检查派生类型是否注册了一个函数。如果是,则调用该函数。如果不是,则要么默默地忽略它,要么需要引发异常。
关于c++ - 使用 RTTI(或返回类型标签的虚函数)是否可行?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54157031/