我想看看是否可以创建“interfaces”,继承它们,然后在运行时检查是否有任何随机类实现了该接口。这就是我所拥有的:
struct GameObject {
int x,y;
std::string name;
virtual void blah() { };
};
struct Airholder {
int oxygen;
int nitrogen;
};
struct Turf : public GameObject, public Airholder {
Turf() : GameObject() {
name = "Turf";
}
void blah() { };
};
void remove_air(GameObject* o) {
Airholder* a = dynamic_cast<Airholder*>(o);
if(!a) return;
a->oxygen = 0;
a->nitrogen = 0;
};
现在,它有效。文档说它工作正常,测试示例有效。但是,直到我向GameObject添加了一个虚方法,它才编译。问题是,我真的不知道该功能是否打算像这样使用。是什么让我想知道我必须为我正在检查的类声明一个虚函数。但显然,没有,我正在检查的类没有虚函数,实际上我的整个代码与虚函数无关,这是一种完全不同的方法。
所以,我想我的问题是:如果我正在做的事情真的有效,为什么我需要一个虚拟函数给我的班级一个vtable?为什么我不能将类声明为“运行时类型”或没有虚函数的东西?
答案 0 :(得分:6)
如果T是“指向cv1 B的指针”并且v具有类型“指向cv2 D的指针”,使得B是D的基类,则结果是 指向由v指向的D对象的唯一B子对象的指针。类似地,如果T是“对cv1 B的引用” 并且v具有类型“cv2 D”,使得B是D的基类,结果是unique60)B子对象的左值 由v引用的D对象。在指针和引用的情况下,cv1应该是相同的cvqualification as,或更高的cv-qualification,cv2和B应该是一个可访问的明确的基类 D. [例如:
struct B {};
结构D:B {};
void foo(D * dp)
{
B * bp = dynamic_cast(dp); //相当于B * bp = dp;
}
- 例子]
制作类型多态,需要虚函数,符合§10.3:
虚函数支持动态绑定和面向对象编程。声明或的类 继承虚函数称为多态类。
所以原因是“因为标准是这么说的”。这并没有真正告诉你为什么标准这样说,但其他答案涵盖了我认为。
答案 1 :(得分:1)
所以,我想我的问题是:如果我正在做的事情真的有效,为什么我需要一个虚拟函数给我的班级一个vtable?为什么我不能将类声明为“运行时类型”或没有虚函数的东西?
虚函数的存在是使C ++中的类具有多态性的原因。 dynamic_cast<>
仅适用于多态类。 (编译器将拒绝非多态对象的动态强制转换。)
多态性在时间和空间(记忆)方面都有成本。对虚拟功能的调用现在是间接的,通常根据虚拟表实现。在一些关键的地方,这些费用是不可接受的。因此,语言提供了避免这些成本的方法。
该语言的其他地方也存在类似的概念。基本原则是,如果你不想使用一些高falutin'功能,你不应该为某些人想要使用它的事实付出代价。
答案 2 :(得分:0)
dynamic_cast
要求类型是多态的,并且没有任何虚拟方法(或至少是虚拟析构函数),类型不是(运行时)多态。简单的继承是不够的。如果记错了,dynamic_cast
使用的运行时类型信息将存储在vtable旁边。
答案 3 :(得分:0)
主要有两个原因。首先是它没有用例。继承点是虚函数。如果您不使用虚函数,请不要使用继承。
第二个是,实际实现dynamic_cast
非常复杂,由于C ++编译模型而无需虚函数。实际实现dynamic_cast
的唯一方法是在虚拟表上操作 - 二进制数据块是无类型的。你可以在一个TU中定义一个类然后只有dynamic_cast
- 现在一个TU认为该类有一个vtable而一个没有。那将是即时的坏事。允许dynamic_cast
对尚未具有虚函数的类进行export
,这意味着“非常难以实现”。
答案 4 :(得分:0)
正如其他人所说,你需要至少一个虚函数才能使类具有多态性。为什么这很重要的是dynamic_cast本身就是一个多态操作!给定基类指针,它会根据调用它的实际对象返回不同的结果。
C ++有一个“不为你不需要的东西买单”的理念,因此除非有虚拟函数存在的需要,否则不提供vtable(或编译器使用的任何机制)。显然,C ++的设计者认为这是对dynamic_cast正常运行的合理要求,或者它们提供了一种在没有它的情况下生成vtable的方法。
答案 5 :(得分:-1)
[编辑]根据评论(人们比我更聪明),我的回答是完全错误的。但是,无论如何都要让你的析构函数变为虚拟。 [/编辑]
在C ++中,我认为如果析构函数是虚拟的,向上转换为基类型是唯一安全的。从技术上讲它是安全的,但实际上,你几乎总是想要一个虚拟的析构函数。例如:
class Base {
int thingy;
};
class Derived : Base{
int *array;
Derived() {array = new int[100];}
~Derived() {delete [] array;}
};
int main() {
std::auto_ptr<Base> obj(dynamic_cast<Base*>(new Derived));
}
在此示例中,当obj超出范围时,auto_ptr会自动调用Base的析构函数,但不会调用Derived deconstructor ,因为类型是Base,而不是Derived。 [编辑:更正]这会导致未定义的行为(在最好的情况下,它会导致内存泄漏)。我不知道为什么C ++不需要虚拟析构函数来编译演员表,它确实应该。