2014-01-10 172 views
-2

那么我的问题有点棘手。 比方说,我有三个类,像这样:从派生类调用基本函数时会发生什么?

class GrandFather 
    { 
    public: 
     virtual int DoSomething() 
     { 
      return 3; 
     } 
    }; 

    class Father : public GrandFather 
    { 
    }; 

    class Child : public Father 
    { 
    public: 
     virtual int DoSomething() 
     { 
      Father::DoSomething();  
     } 
    }; 

我已经在一些参考文献中,在调用基本功能时,整个虚拟机制不使用,名称重整发生。

现在我想知道如何做到这一点。

Child类有一个指向它自己的vtable的指针,这将指示在调用DoSomething()时调用子对象的实现。

父类也有一个指向它自己的vtable的指针,这将指示在调用DoSomething()时调用GrandFather的实现。

当我在儿童内部使用Father::DoSomething()时,应该调用GrandFather::DoSomething(), 但儿童怎么能告诉功能在哪里?

如果真的使用名字mangling,那么怎么样?因为没有该名称的功能(类似_Father_DoSomething(this))。

孩子必须访问父亲的vptr才能到达GrandFather::DoSomething(),据我所知,他不能。

这一直困扰我很长一段时间,所以我会非常感谢帮助。 谢谢:)

+0

你尝试了什么!编译错误,运行时错误,具体! –

回答

1

每当你明确限定成员函数,你就是说你想在那个特定的类中调用那个特定函数,并且不使用虚拟调用机制。虚拟调用仅在您调用成员函数而未对其进行限定时发生,它使用虚拟机制来决定要使用哪个类的成员函数。

调用Father::DoSomething调用GrandFather :: DoSomething的事实是由于继承,这是一个单独的机制。如果您引用派生类的成员并且它不在那里,它会上升一级并在基类中查找它。在那里不需要虚拟呼叫。

+0

无论何时您明确具有资格,您都要告诉编译器从那时开始进行名称查找。就像在他的例子中,函数可能不在他指定的类中,而是在该类的父类中。 –

+0

@JamesKanze:是的,好点。我已经为我的答案添加了一些关于继承的信息。 –

1

当您从Child调用Father::DoSomething时,编译器使用编译时间信息来确定要调用的内容。由于Father中没有版本,因此它知道调用GrandFather中的版本,因为它的版本在Father范围内有效。

当重写的方法调用基本实现时,不使用vtable。 vtable仅在通过指针或引用调用方法时使用。这就是C++中所有多态性的实现方式。

名称mangling与此没有任何关系。 Name mangling基本上是如何对一个方法名进行编码的,因此为方法/函数提供一个唯一的名称。没有两个方法/函数将具有相同的损坏名称。

+0

vtable(假定这是所使用的实现技术)仅在以下情况下使用:1)静态查找找到的函数是虚拟的,2)函数名称不合格。实际上,如果编译器可以确定动态类型(如果没有指针或引用,则始终是这种情况),编译器也将放弃使用该表。 –

+0

@JamesKanze - 是的,但是当从重写的函数中调用基类函数时,不需要使用vtable,因为可以静态地确定正确的实现。 – Sean

1

当合格的名称是用来命名的功能,称为在编译时被确定,使用静态类型的 表达功能 ,和名称查找在由给定的限定符的 位置开始。由于要被调用的函数完全通过静态查找确定为 ,因此根本不需要访问任何vtable 。

1

g ++ file_name.cpp -S命令可以提供足够的信息来清除所有与vtable相关的问题。请检查生产.s文件:

的孩子有自己的包含父虚函数表是虚函数表,在自身的次序,包含虚函数表的爷爷:

vtable for Child: 
    Child::DoSomething() 
    vtable for Father 

vtable for Father: 
    GrandFather::DoSomething() 
    vtable for GrandFather 

vtable for GrandFather: 
    GrandFather::DoSomething() 

当我使用父:: DoSomething的()内孩子,它应该叫 GrandFather :: DoSomething(),但孩子怎么能告诉功能 是哪里?

虚函数表的父亲有参考祖父:: DoSomething的(),因此调用父:: DoSomething的()你是actualli调用爷爷:: DoSomething的()

1
  1. “名称重整” 有完全与它无关。被称为“名称修改”的技术属于完全不同且不相关的领域。我不知道你有什么想法让它参与到这里。

  2. 类没有任何“指向vtable”的指针。只有特定对象(又名实例)类类型可能有这样的指针。请勿混用类别类别类型为的对象。无论如何,“vtable指针”是语言级别不存在的实现细节。理解语言层面的代码行为是完全没有必要的。

  3. 在您的示例中,根本没有任何对象。你只是宣布了一堆类。出于这个原因,早期采取任何“指向vtable的指针”。你在发布的内容中没有任何指向任何vtables的指针。

  4. 当涉及到Father::DoSomething()调用的问题时,任何“虚拟表”的问题根本不会出现,即使作为实现细节。 Father::DoSomething()呼叫使用合格目标函数的名称。这种合格的调用总是直接解决,而不涉及任何vtable。即通过执行Father::DoSomething(),您明确要求编译器忽略任何多态性(忽略任何“vtables”)并直接在类Father中为名称DoSomething执行编译时名称查找。根据名称查找规则,它会找到函数GrandFather::DoSomething()并调用它。

  5. 如果你确实声明对象,像

    Child child; 
    

    ,那么你将有一个GrandFather子对象嵌入嵌入上述Child对象Father子对象。这些嵌套对象中的所有“vtable指针”都将指向Child的vtable(事实上,在典型的实现中,所有这些嵌套对象将共享一个vtable指针)。

    现在,如果你调用

    child.DoSomething() 
    

    这个调用将按照Child虚函数表来解决,并派遣到Child::DoSomething()(大多数编译器实际上是足够聪明的优化代码,并直接派遣通话)。但是,正如我上面所说的那样,从Child::DoSomething内拨打Father::DoSomething的合格呼叫将按照您的要求直接无条件执行。它不关心任何vtable指针。它只是直接到GrandFather::DoSomething()

这就是它的全部。

相关问题