使用 RTTI(或返回类型标记的虚函数)是否正常
Is using RTTI (or virtual functions that return a type tag) ever OK?
使用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
不仅可以,而且在许多情况下都是必不可少的。
当我看到这样的代码时,我使用开闭原则作为指导。
如果我必须在将新的派生类型添加到系统时重新访问该if-else
块或enum
,我认为这是一个问题。如果没有,我不认为这是一个问题。
当您看到级联if-else
代码块时,它通常违反了开闭原则,应避免使用。避免这种情况的方法是使用回调机制。
- 让基类有一个函数来注册派生类型的回调函数。
- 在基类的业务逻辑中,检查是否已为派生类型注册函数。如果是,请调用该函数。否则,要么以静默方式忽略它,要么需要引发异常。
相关文章:
- 函数是否可以访问传递给main()的参数
- 根据某个函数是否存在启用模板
- 无论如何,我可以确定构造函数是否存在吗?
- 在 nullptr 上调用无状态类的非静态成员函数是否合法?
- 如何检查函数是否在LLVM Instrumentation pass的ModulePass的系统头文件中定义?
- 如果 C 函数仍然可以间接执行(通过回调函数),那么将它声明为静态函数是否是一种不好的做法?
- 在对象构造期间,将指向尚未构造的子对象的指针传递给另一个子对象的构造函数是否危险?
- 某些 boost::asio 异步函数是否将处理程序连接到操作,以便处理程序被触发一次?
- 调用 erase() 函数是否也会在擦除元素之前更改迭代器值?
- 由并发无序映射查找线程调用的函数是否安全?
- 使用静态成员函数而不是普通函数是否有任何开销?
- 从其存储的回调中删除 std::函数是否安全
- 析构函数是否会自动调用 delete[] C++?
- 构造函数是否有一种现代C++方法来了解其'container'类?
- 循环中本地对象的析构函数是否保证在下一次迭代之前被调用?
- 移动构造函数是否C++过时?
- 在 c++ 中将对象设置为等于同一类的构造函数是否有效?
- 此函数是否会在C++中创建内存泄漏?
- 具有默认值的单个参数构造函数是否与默认构造函数相同?
- 如何检测构造函数是否与抛出的析构函数无关