类型转换派生类作为基类导致基类成员函数调用

时间:2014-07-11 06:33:45

标签: c++ inheritance

在基类和派生类具有相同名称的成员函数的情况下,为什么要将派生类类型转换为基类并调用该成员实际生成对应于基类的输出?我认为派生类中的成员函数定义覆盖了基类中的函数。

在下面的代码中,有一个基类myClass和一个派生类newClassmyClassnewClass都实现了名为serialize().

的成员函数

insta类型的实例newClass被类型转换为myClass时,被调用的serialize()成员函数属于基类myClass而不是派生类。两个调用的地址相同,这表明没有发生任何神秘的复制事件,这确实与类型转换有关。

为什么会这样?

以下是重现此行为的代码。

#include <iostream>
#include <typeinfo>
#include <string>
#include <sstream>
#include <vector>

class myClass{

friend void Inspect(myClass&);
private:
    int data;
    std::string name;
public:
    myClass(std::string _name, int _d):
        name(_name),
        data(_d){}
    std::vector<double> v;
    std::string serialize();

};


std::string myClass::serialize(){
    std::ostringstream retstream;
    retstream << "string name " << name << "\n";
    retstream << "int data " << data << "\n";
    retstream << "vector<double> v ";
    for (unsigned int i = 0; i < v.size(); i++){
        retstream << v[i] << " ";
    }
    retstream << "\n";
    retstream << "---" << std::endl;
    retstream << "address " << this << "\n";
    return retstream.str();
}

class newClass: public myClass{

private:
    int data2;
    std::string name;
public:
    newClass(std::string _name, int _d):
        myClass(_name, _d), name(_name), data2(_d){}
    std::string serialize();
};


std::string newClass::serialize(){
    std::ostringstream retstream;
    retstream << "string name " << name << "\n";
    retstream << "address " << this << "\n";
    return retstream.str();
}


template <class T>
void Inspect(T & instance){
//#if defined(DDEBUG) || defined(DDDEBUG)
    std::cout << "Inspecting " << typeid(instance).name() << "\n";
    std::cout << instance.serialize() << "\n";
    std::cout << &instance << "\n";
//#endif
}


int main(){
newClass insta("newClass_instance-1", 2);
Inspect<myClass>(insta);
Inspect<newClass>(insta);
return 0;}

在编译此代码并运行它时,可以获得此输出:

Inspecting 7myClass
string name newClass_instance-1
int data 2
vector<double> v 
---
address 0x7fff73529fb0

0x7fff73529fb0
Inspecting 8newClass
string name newClass_instance-1
address 0x7fff73529fb0

0x7fff73529fb0

我的编译器信息是g++ (Ubuntu 4.8.2-19ubuntu1) 4.8.2

1 个答案:

答案 0 :(得分:2)

将成员声明为虚拟成员。将成员声明为虚拟会向您的类添加隐藏的vtable对象。 vtable基本上是一个指向函数的指针数组(每个编译器都会以不同的方式实现)。当您调用虚函数时,vtable用于调用该函数。当您调用非虚拟类函数时,类的类型用于调用该函数。

class Base {
  virtual void VirtualFunc() { }
  void Func() { }
};
class Derived : public Base {
  void VirtualFunc() override { }
  void Func() override { }
};

Base* pBase = new Base();

pBase->VirtualFunc();                       // calls Base's VirtualFunc
((Derived*)pBase)->VirtualFunc();           // calls Base's VirtualFunc
pBase->Derived::VirtualFunc();              // wont compile
((Derived*)pBase)->Derived::VirtualFunc();  // will crash
pBase->Func();                              // calls Base's Func
((Derived*)pBase)->Func();                  // will crash

Derived* pDerived = new Derived();

pDerived->VirtualFunc();                    // calls Derived's VirtualFunc
((Base*)pDerived)->VirtualFunc();           // calls Derived's VirtualFunc
pDerived->Base::VirtualFunc();              // calls Base's VirtualFunc
((Base*)pDerived)->Base::VirtualFunc();     // calls Base's VirtualFunc
pDerived->Func();                           // calls Derived's Func
((Base*)pDerived)->Func();                  // calls Base's Func