c++ - 静态成员重载和覆盖如何在 C++ 结构中工作?
问题描述
这是我最近在虚幻引擎中看到的一个示例代码。
用例是层次结构中的给定结构将其“类 id”在层次结构中作为静态整数字段。
使用辅助函数作为成员,通过比较 ClassID 字段来识别结构的“类型”。
这些结构旨在始终存储为对象变量,而不是指针
struct ENGINE_API FDamageEvent
{
static const int32 ClassID = 0;
virtual int32 GetTypeID() const { return FDamageEvent::ClassID; }
virtual bool IsOfType(int32 InID) const { return FDamageEvent::ClassID == InID; };
}
struct ENGINE_API FPointDamageEvent : public FDamageEvent
{
/** ID for this class. NOTE this must be unique for all damage events. */
static const int32 ClassID = 1;
virtual int32 GetTypeID() const override { return FPointDamageEvent::ClassID; };
virtual bool IsOfType(int32 InID) const override { return (FPointDamageEvent::ClassID == InID) || FDamageEvent::IsOfType(InID); };
}
struct ENGINE_API FRadialDamageEvent : public FDamageEvent
{
/** ID for this class. NOTE this must be unique for all damage events. */
static const int32 ClassID = 2;
virtual int32 GetTypeID() const override { return FRadialDamageEvent::ClassID; };
virtual bool IsOfType(int32 InID) const override { return (FRadialDamageEvent::ClassID == InID) || FDamageEvent::IsOfType(InID); };
}
所以这里的问题是
- 是否
ClassID
在子结构中重新分配、重新声明或隐藏字段? - 为什么
GetTypeID
函数在检索时使用自身的显式范围,ClassID
尽管它是虚拟的? - 为什么
IsOfType
子结构中的函数调用父类的IsOfType
函数?是否只是为了模拟每个孩子也是其父类型的多态性?
解决方案
根据 c++ 标准[class.derived.2]:
除非在派生类中重新声明,否则基类的成员也被视为派生类的成员。
这意味着如果您不在派生类中重新声明classId
,当您在派生类中访问它时,您实际上是在使用基类之一。
但是,在您的情况下,您确实重新声明了classId
. 在这种情况下,classId
ofbaseClass
不被视为派生类的成员,因此classId
在派生类范围内访问(没有解析限定符FDamageEvent::
)将引用classId
您在派生类中声明的内容。
你可以把这个名字叫做shadowing(overloading
让人想到函数)。
但请记住:静态变量从不涉及多态性!(除非你正在做静态多态性,那么那将是另一回事)。多态性依赖于实例,静态成员不与任何实例相关联!.
使用实例或->
实例(或指向实例的指针)访问静态成员是指实例的编译时类型的静态变量,而不是其多态类型(这两者在 c++ 中是不同的)。考虑以下代码:
struct Base {
static const int id = 0;
virtual int get_id() const {
return id;
};
};
struct Derived : Base {
static const int id = 1;
int get_id() const override {
return id;
}
};
int main() {
Base* b = new Derived;
std::cout << b->id << std::endl; // Output: 0
std::cout << b->get_id() << std::endl; // Output: 1
return 0;
}
根据问题 2,此处不需要使用显式范围:classId
并且FRadialDamageEvent::classId
是等效的。这只是代码品味的问题。为了清楚起见,有些人更喜欢后者。
根据问题 3,我不太了解这段代码的意图。想象一下,你有一个多级继承层次结构,例如A -> B -> C
(X -> Y
reads Y derives from X
),然后根据这个方法,一个C
is not的实例B
,这违反了传统的面向对象模式。
关于关键字override
:它只是一个关键字,以确保您实际上覆盖了基类的虚拟方法。virtual
如果使用,则不必编写override
,因为后者已经使函数成为虚拟函数。如果该函数实际上不是基类中的虚函数,那么编译器会发出一个错误:这可以防止声明新虚方法的拼写错误,而不是实际覆盖一个虚方法,并且更清晰。
推荐阅读
- matlab - Matlab随机数rng:选择种子
- javascript - 禁用提交按钮,直到重新获取 recaptcha3 g-recaptcha-response 值
- css - Squarespace 日历颜色更改的自定义 CSS
- r - R:将连续日期从单列转换为 2 列范围
- css - 弹性盒屏幕的 IE11 问题不适合 Angular8
- html - 如何在引导程序 4.3.1 中获取日期选择器
- database - 当我停止程序并重新启动它时,我的 DataGridView 会暂时更新。但我的 Access 数据库仍然永久更新
- android - 为什么 Flutter App 不能在真机上运行
- java - 在日/夜模式之间切换时启用平滑的活动过渡
- javascript - target="_blank" 在 chrome 中不起作用?