一般来说,在其他语言中,多态就是父类指针指向了子类对象 但是对于c++来说,对象不一定是在堆区,还有可能在栈区,那么这时候的多态会是什么样的呢?
c++中的多态分为 静态多态 和 动态多态 静态多态:实质上就是函数重载 一般说的多态实质上指的是 动态多态
先来看一个例子
class Father{ public: void who(){ cout << "father" << endl; } }; class Son:public Father{ void who(){ cout << "son" << endl; } }; void say(Father& obj){ obj.who(); } int main(int argc, const char * argv[]) { Son son; say(son); }输出为:father 因为普通成员函数的调用和全局函数实质上没有上面区别,在编译的时候就已经决定调用Fahter的say函数,称之为静态绑定。 如果想要输出为:son,就要使用动态绑定,将who声明为 virtual
class Father{ public: virtual void who(){ cout << "father" << endl; } }; class Son:public Father{ public: void who(){ cout << "son" << endl; } }; void say(Father& obj){ obj.who(); } int main(int argc, const char * argv[]) { Son son; say(son); }为什么会出现这样的情况呢?
如果who没有加virtual,Father对象的内存大小为1个字节。如果加了virtual,Father对象的大小变为4个字节,因为对象内部多了一个 vfptr 指针,指向该类对应的虚函数表。Son继承于Father,因此也继承了父类的 vfptr 指针和虚函数表,由于是继承的父类的vfptr,所以一开始子类的vfptr指向的是父类的虚函数表(指针内容相同),但在对象的构造函数中,会将vfptr指向子类自己的虚函数表。如果没有重写父类的 say 函数,子类虚函数表中say和父类虚函数表中的say是一样的。如果重写了父类的say函数,就会用重写的say函数替换虚函数表中的say函数。虚函数表是动态联编的,运行的时候,才会通过虚函数指针找到虚函数表中对应的函数。
虚析构函数用于解决多态时,子类释放不干净的情况 在使用多态的时候,由于使用了父类指针指向了子类对象,因此在调用析构函数的时候,只会调用父类的析构函数。这个时候就需要将析构函数转化为虚函数。
class Father{ virtual ~Father(){}; } class Child{ ~Child(){}; } int main(){ Father* c = new Child; // 多态 delete c; // 因为Father的析构函数是虚函数,因此c释放的时候会同时调用子类和父类的析构函数 }纯虚析构函数 类内声明,类外实现加粗样式
class Father{ virtual ~Father(){} = 0; } Father::~Father(){};有纯虚析构函数的类,也是虚类,因此不能实例化。
析构函数和纯虚析构函数其实差不多,都要实现,只是一个在类内,一个在类外,另外,有纯虚析构函数的类是虚类,不能被实例化。
将子类指针或引用转化为父类是安全的
Son* s = new Son; Father* f = (Father*)s;因为父类指针的寻址范围总是小于子类(多态就是这种情况) 但是反过来就不安全了 不安全
Father* f = new Father; Son* s = (Father*)f;如果将父类指针强转为子类,子类指针的寻址范围会超出父类的范围