虚函数/继承/虚继承下的内存图
-
概念:虚函数,是指被virtual关键字修饰的成员函数。
-
作用:函数作为多态的实现方式,重要性毋庸置疑。多态意指相同的消息给予不同的对象会引发不同的动作(一个接口,多种方法)
-
虚函数的内存模型:虚函数(Virtual Function)是通过虚函数表(Virtual Table,简称为V-Table)来实现的。虚函数表主要存储的是指向一个类的虚函数地址的指针,通过使用虚函数表,继承、覆盖的问题都都得到了解决。
- 单继承无虚函数覆盖的情况:
#include <iostream> #include <string> class Base{ public: virtual void f(){ std::cout << "Base::f()" << std::endl; } virtual void g(){ std::cout << "Base::g()" << std::endl; } virtual void h(){ std::cout << "Base::h()" << std::endl; } }; class Derived : public Base{ public: virtual void f1(){ std::cout << "Derived::f1()" << std::endl; } virtual void g1(){ std::cout << "Derived::g1()" << std::endl; } virtual void h1(){ std::cout << "Derived::h1()" << std::endl; } };
- 单继承有虚函数覆盖的情况
#include <iostream> #include <string> class Base{ public: virtual void f(){ std::cout << "Base::f()" << std::endl; } virtual void g(){ std::cout << "Base::g()" << std::endl; } virtual void h(){ std::cout << "Base::h()" << std::endl; } }; class Derived : public Base{ public: virtual void f1(){ std::cout << "Derived::f1()" << std::endl; } virtual void g(){ std::cout << "Derived::g1()" << std::endl; } virtual void h1(){ std::cout << "Derived::h1()" << std::endl; } };
- 多重继承的情况
#include <iostream> #include <string> class Base1 { public: virtual void f(){ std::cout << "Base1::f()" << std::endl; } virtual void g(){ std::cout << "Base1::g()" << std::endl; } virtual void h(){ std::cout << "Base1::h()" << std::endl; } }; class Base2 { public: virtual void f(){ std::cout << "Base2::f()" << std::endl; } virtual void g(){ std::cout << "Base2::g()" << std::endl; } virtual void h(){ std::cout << "Base2::h()" << std::endl; } }; class Base3 { public: virtual void f(){ std::cout << "Base3::f()" << std::endl; } virtual void g(){ std::cout << "Base3::g()" << std::endl; } virtual void h(){ std::cout << "Base3::h()" << std::endl; } }; class Derived : public Base1, public Base2, public Base3 { public: virtual void f(){ std::cout << "Derived::f()" << std::endl; } virtual void g1(){ std::cout << "Derived::g1()" << std::endl; } virtual void h1(){ std::cout << "Derived::h1()" << std::endl; } };
虚函数表
- 多层继承的情况
#include <iostream> #include <string> class Base { public: virtual void f(){ std::cout << "Base::f()" << std::endl; } virtual void g(){ std::cout << "Base::g()" << std::endl; } virtual void h(){ std::cout << "Base::h()" << std::endl; } }; class Derived : public Base { public: virtual void f(){ std::cout << "Derived::f()" << std::endl; } virtual void g1(){ std::cout << "Derived::g1()" << std::endl; } }; class DDerived : public Derived { public: virtual void f(){ std::cout << "DDerived::f()" << std::endl; } virtual void h(){ std::cout << "DDerived::h()" << std::endl; } virtual void g2(){ std::cout << "DDerived::g2()" << std::endl; } }; int main(int argc, char* argv[]) { DDerived dd; dd.f(); }
菱形继承问题:C++的多继承是指从多个直接基类中产生派生类的能力,多继承的派生类继承了所有父类的成员。从概念上来讲这是非常简单的,但是多个基类的相互交织可能会带来错综复杂的设计问题,命名冲突就是不可回避的一个,比如典型的是菱形继承,如图所示:
代码:
#include <iostream>
#include <stdint.h>
class A{
public:
long a;
};
class B: public {
public:
long b;
};
class C: public A{
public:
long c;
};
class D: public B, public C{
public:
void seta(long v) { a = v; } // 命名冲突
void setb(long v) { b = v; } // 正确
void setc(long v) { c = v; } // 正确
void setd(long v) { d = v; } // 正确
private:
long d;
};
int main(int argc, char* argv[])
{
D d;
}
- 为了解决多继承时命名冲突和冗余数据的问题,C++提出了虚继承这个概念,虚继承可以使得在派生类中只保留一份间接基类的成员。使用方式就是在继承方式前面加上virtual关键字修饰,示例代码如下(基于前面的例子修改):
#include <iostream>
#include <stdint.h>
class A{
public:
long a;
};
class B: virtual public A{
public:
long b;
};
class C: virtual public A{
public:
long c;
};
class D: public B, public C{
public:
void seta(long v) { a = v; } // 现在不会冲突了
void setb(long v) { b = v; } // 正确
void setc(long v) { c = v; } // 正确
void setd(long v) { d = v; } // 正确
private:
long d;
};
int main(int argc, char* argv[]){
D d;
}
继承图:
类B的内存布局:
类D的内存布局: