带虚函数的多基派生
大约 4 分钟
带虚函数的多基派生
#include <iostream>
using std::cout;
using std::endl;
class Base1
{
public:
Base1()
: _iBase1(10)
{
}
virtual
void f()
{
cout << "Base1::f()" << endl;
}
virtual
void g()
{
cout << "Base1::g()" << endl;
}
virtual
void h()
{
cout << "Base1::h()" << endl;
}
private:
int _iBase1;
};
class Base2
{
public:
Base2()
: _iBase2(100)
{
}
virtual void f()
{
cout << "Base2::f()" << endl;
}
virtual
void g()
{
cout << "Base2::g()" << endl;
}
virtual
void h()
{
cout << "Base2::h()" << endl;
}
private:
int _iBase2;
};
class Base3
{
public:
Base3()
: _iBase3(1000)
{
}
virtual
void f()
{
cout << "Base3::f()" << endl;
}
virtual
void g()
{
cout << "Base3::g()" << endl;
}
virtual
void h()
{
cout << "Base3::h()" << endl;
}
private:
int _iBase3;
};
class Derived
: public Base1
, public Base2
, public Base3
{
public:
Derived()
: _iDerived(10000)
{
}
void f()
{
cout << "Derived::f()" << endl;
}
virtual
void g1()
{
cout << "Derived::g1()" << endl;
}
private:
int _iDerived;
};
int main()
{
Derived d;
Base2 *pBase2 = &d;
Base3 *pBase3 = &d;
Derived *pDerived = &d;
pBase2->f();
cout << "sizeof(d) = " << sizeof(d) << endl;
cout << "&Derived = " << &d << endl;
cout << "pBase2 = " << pBase2 << endl;
cout << "pBase3 = " << pBase3 << endl;
return 0;
}
//结论:多重继承(带虚函数)
//1. 每个基类都有自己的虚函数表
//2. 派生类如果有自己的虚函数,会被加入到第一个虚函数表之中
//3. 内存布局中,其基类的布局按照基类被声明时的顺序进行排列
//4. 派生类会覆盖基类的虚函数,只有第一个虚函数表中存放的是
// 真实的被覆盖的函数的地址;其它的虚函数表中存放的并不是真实的
// 对应的虚函数的地址,而只是一条跳转指令
多基派生的二义性
class A
{
public:
virtual
void a()
{
cout << "a() in A" << endl;
}
virtual
void b()
{
cout << "b() in A" << endl;
}
virtual
void c()
{
cout << "c() in A" << endl;
}
};
class B
{
public:
virtual
void a()
{
cout << "a() in B" << endl;
}
virtual
void b()
{
cout << "b() in B" << endl;
}
void c()
{
cout << "c() in B" << endl;
}
void d()
{
cout << "d() in B" << endl;
}
};
class C
: public A
, public B
{
public:
virtual
void a()
{
cout << "a() in C" << endl;
}
void c()
{
cout << "c() in C" << endl;
}
void d()
{
cout << "d() in C" << endl;
}
};
void test()
{
C c;
printf("&c: %p\n", &c);
c.b();
cout << endl;
A *pA = &c;
printf("pA: %p\n", pA);
pA->a();
pA->b();
pA->c();
cout << endl;
B *pB = &c;
printf("pB: %p\n", pB);
pB->a();
pB->b();
pB->c();
pB->d();
cout << endl;
C *pC = &c;
printf("pC: %p\n", pC);
pC->a();
pC->b();//此处就是二义性
pC->c();//此处的c()走的是虚函数机制还是非虚函数机制,如何判别?
pC->d();//此处是隐藏,不是重写
return 0;
}
菱形继承
#include <iostream>
using std::cout;
using std::endl;
class B
{
public:
B()
: _ib(10)
, _cb('B')
{
cout << "B()" << endl;
}
B(int ib, char cb)
: _ib(ib)
, _cb(cb)
{
cout << "B(int,char)" << endl;
}
virtual
void f()
{
cout << "B::f()" << endl;
}
virtual
void Bf()
{
cout << "B::Bf()" << endl;
}
private:
int _ib;
char _cb;
};
class B1
: virtual public B
{
public:
B1()
: _ib1(100)
, _cb1('1')
{
}
B1(int ib, char ic, int ib1, char cb1)
: B(ib, ic)
, _ib1(ib1)
, _cb1(cb1)
{
cout << "B1(int,char,int,char)" << endl;
}
virtual
void f()
{
cout << "B1::f()" << endl;
}
virtual
void f1()
{
cout << "B1::f1()" << endl;
}
virtual
void Bf1()
{
cout << "B1::Bf1()" << endl;
}
private:
int _ib1;
char _cb1;
};
class B2
: virtual public B
{
public:
B2()
: _ib2(1000)
, _cb2('2')
{
}
B2(int ib, char ic, int ib2, char cb2)
: B(ib, ic)
, _ib2(ib2)
, _cb2(cb2)
{
cout << "B2(int,char,int,char)" << endl;
}
//virtual
void f()
{
cout << "B2::f()" << endl;
}
//virtual
void f2()
{
cout << "B2::f2()" << endl;
}
//virtual
void Bf2()
{
cout << "B2::Bf2()" << endl;
}
private:
int _ib2;
char _cb2;
};
class D
: public B1
, public B2
{
public:
D()
: _id(10000)
, _cd('3')
{
}
D(int ib, char cb
, int ib1, char ib
, int ib2, char cb2
, int id, char cd)
: B1(ib1, ib1)
, B2(ib2, cb2)
, B(ib, cb)
, _id(id)
, _cd(cd)
{
cout << "D(...)" << endl;
}
virtual
void f()
{
cout << "D::f()" << endl;
}
virtual
void f1()
{
cout << "D::f1()" << endl;
}
virtual
void f2()
{
cout << "D::f2()" << endl;
}
virtual
void Df()
{
cout << "D::Df()" << endl;
}
private:
int _id;
char _cd;
};
void test(void)
{
D d;
cout << sizeof(d) << endl;
B1 *pb1 = &d;
pb1->f();
B2 *pb2 = &d;
pb2->f();
d.f();
return 0;
}
//结论:虚基指针所指向的虚基表的内容
// 1. 虚基指针的第一条内容表示的是该虚基指针距离所在的子对象的首地址的偏移
// 2. 虚基指针的第二条内容表示的是该虚基指针距离虚基类子对象的首地址的偏移
如果是在若干类层次中,从虚基类直接或间接派生出来的派生类的构造函数初始化列表均有对该虚基类构造函数的调用,那么创建一个派生类对象的时候只有该派生类列出的虚基类的构造函数被调用,其他类列出的将被忽略,这样就保证虚基类的唯一副本只被初始化一次。即虚基类的构造函数只被执行一次。
对于虚继承的派生类对象的析构,析构函数的调用顺序为:
- 先调用派生类的析构函数;
- 然后调用派生类中成员对象的析构函数;
- 再调用普通基类的析构函数;
- 最后调用虚基类 的析构函数。