template<class T>
class Object
{
private:
T value;
public:
Object(T x = T()) { value += 1; }
static int num;
};
template<class T>
int Object<T>::num = 0;
class Base:public Object<int>
{
public:
Base() { num += 1; }
void Print() { cout << "Base:" << num << endl; }
};
class Test :public Object<int>
{
public:
Test() { num += 1; }
void Print() { cout << "Test:" << num << endl; }
};
int main()
{
Base t1, b1;
Test t2, b2;
t1.Print(); //此时静态成员变量,存放在数据区,模板类的类型为
//在类外进行初始化,且进初始化一次,因此t1,b1,t2,b2操作的都是同一个num
t2.Print();
}
如下图:
结果:
2.base用double类型继承,test用int类型继承,会推演出两个模板,自然而然,在各自的类型模板中,对静态成员变量只初始化一次
class Base:public Object<double>
{};
class Test :public Object<int>
{};
结果:
class Object
{
private:
int value;
public:
Object(int x = 0) :value(x)
{}
};
class Base:public Object
{
int num;
public:
Base(int x = 0) :num(x), Object(x + 10) {}
};
class Test :public Object
{
int sum;
public:
Test(int x = 0):sum(x),Object(x+10){}
};
class Det :public Base, public Test
{
int total;
public:
Det(int x = 0) :total(x), Base(x + 10), Test(x + 20), Object(x + 100) {}
};
int main()
{
Det d(0);
Base b1 = d;//ok
Test t1 = d;//ok
Object op = d;//error op指向d,d继承了Base和Test,所以op不知道指向的是继承base的d还是继承了Test的d,
}
从下面的对象成员模型构造,可以看出菱形继承有数据冗余和二义性的问题。在Det的对象中Object成员会有两份。
虚继承:主要是通过虚继承,在每个派生类中会有一个虚基类指针(占四个字节),和虚基类表(不占空间);当虚继承的派生类被当做基类再次虚继承,虚基类指针也会被继承。
(因此每个派生类既可以通过虚基类指针,查找虚基类表找到中的成员)
1. 一个类可以在一个类族中用作虚基类,也可以用作非虚基类。
2. 在派生类的对象中,同名的虚基类**只产生一个虚基类子对象**,而某个非虚基类产生各自的对象。
3. 虚基类子对象是由最派生类(最后派生出来的类)的构造函数通过调用虚基类的构造函数进行初始化 (最派生类会先去调用虚基类的构造函数)。
4. 在派生类的构造函数的成员初始化列表中,必须列出对虚基类构造函数的调用,如果没有列出,则表示使用该虚基类的缺省构造函数。
5. 在一个成员初始化列表中,同时出现对虚基类和非虚基类构造函数的调用时,虚基类的构造函数先于非虚基类的构造函数执行。
6. 虚基类并不是在声明基类时声明的,而是在声明派生类是,指定继承方式时声明的。因为一个基类可以在生成一个派生类作为虚基类,而在生成另一个派生类时不作为虚基类。
//虚基类
class Object
{
private:
int value;
public:
Object(int x = 0) :value(x)
{}
};
class Base:virtual public Object
{ //存在虚基类指针和虚基类表
int num;
public:
Base(int x = 0) :num(x), Object(x + 10) {}
};
class Test :virtual public Object
{ //存在虚基类指针和虚基类表
int sum;
public:
Test(int x = 0):sum(x),Object(x+10){}
};
class Det :public Base, public Test
{
int total;
public:
//虚基类子对象是由最派生类(最后派生出来的类)的构造函数通过调用虚基类的构造函数进行初始化 (最派生类会先去调用虚基类的构造函数)。 优先初始化Object
Det(int x = 0) :total(x), Base(x + 10), Test(x + 20), Object(x + 100) {}
};
int main()
{
Det d(0);
}
步骤:
class Object
{
private:
int value;
public:
Object(int x = 0) :value(x)
{}
virtual void fun() {}
virtual void add() {}
};
class Base :public Object
{
int num;
public:
Base(int x = 0) :num(x), Object(x + 10) {}
virtual void fun() {}
};
class Test :public Object
{
int sum;
public:
Test(int x = 0) :sum(x), Object(x + 10) {}
virtual void fun() {}
void add() {}
};
class Det :public Base, public Test
{
int total;
public:
Det(int x = 0) :total(x), Base(x + 10), Test(x + 20){}
void fun() {}
void add() {}
};
Object基类中的虚表指针,开始指向Object中的虚表,
当程序运行到,构建Det对象,因为Det继承了Base和Test类,
用两种方式去分别继承Base和Test类,因此Object中的虚表指针,在指向Det中的虚表时,有两个,一个继承Base中的虚表,另一个是继承test中的虚表