dynamic_cast真的适用于多重继承吗?

时间:2011-08-29 21:53:45

标签: c++ polymorphism multiple-inheritance

我想看看是否可以创建“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?为什么我不能将类声明为“运行时类型”或没有虚函数的东西?

6 个答案:

答案 0 :(得分:6)

该标准的第5.2.7段说:

  1. 表达式dynamic_cast(v)的结果是将表达式v转换为type的结果 T. T应该是对完整类类型的指针或引用,或“指向cv void的指针”。类型不应该 在dynamic_cast中定义。 dynamic_cast运算符不应抛弃constness(5.2.11)。
  2. 如果T是指针类型,则v应该是指向完成类类型的指针的右值,结果是一个rvalue: 类型T.如果T是引用类型,则v应该是完整类类型的左值,结果是左值 T。提到的类型。
  3. 如果v的类型与所需的结果类型相同(为方便起见,将在此处称为R. 除了R中的类对象类型比类更符合cv之外,它与R相同 v中的对象类型,结果为v(必要时转换)。
  4. 如果v的值是指针大小写中的空指针值,则结果为类型R的空指针值。
  5. 如果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;
    }
    - 例子]

  6. 否则,v应为多态类型的指针或左值(10.3)。
  7. 制作类型多态,需要虚函数,符合§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 ++不需要虚拟析构函数来编译演员表,它确实应该。