1、第一想法大家都是,既然不能被继承,也就是一旦继承这个类之后,类就不能被初始化。
2、可能把类的构造函数私有化,和单例有点像,但是这样类就不能正常使用了。
3、实在无力,查了下,他们是通过使用虚继承解决这个问题的。
对于虚继承,具体资料见:http://blog.163.com/xiangzaihui@126/blog/static/166955749201182294317243/
里面有一句话很重要:C++规定:把真正创建对象时所指定的类称为是最派生类,虚基类子对象是由最派生类的构造函数通过调用虚基类的构造函数进行初始化的;
具体实现代码如下:
1: #include <cstdio>
2: #include <iostream>
3:
4: template<class T> class CFinal{
5: friend T;
6: private:
7: CFinal(){ std::cout << "CFinal()" << std::endl; }
8: ~CFinal(){}
9: };
10:
11: class CFoo : virtual public CFinal<CFoo>{
12: public:
13: CFoo(){ std::cout << "CFoo()" << std::endl; }
14: ~CFoo(){}
15: };
16:
17: class CBar : public CFoo{
18: public:
19: CBar() { std::cout << "CBar" << std::endl; }
20: };
21:
22: int main()
23: {
24: //CBar bar;
25: CFoo foo;
26: while(1){}
27: return 0;
28: }
即:
template<class T> class CFinal{
friend T;
private:
CFinal(){ std::cout << "CFinal()" << std::endl; }
~CFinal(){}
};
class CFoo : virtual public CFinal<CFoo>{
public:
CFoo(){ std::cout << "CFoo()" << std::endl; }
~CFoo(){}
};
class CBar : public CFoo{
public:
CBar() { std::cout << "CBar" << std::endl; }
};
当你创建CBar时,需要初始化CFinal,但是你不是他的friend,不能初始化(上面标红)
如果你会问不要virtual行不行?
不行,由于不要virtual,你可以经过CFoo的friend访问到CFinal,但是有virtual就不同了,你必须直接初始化CFinal(这是C++的规定,避免多次初始化)
但是有个问题,gcc不能编译通过,在friend T出错,vs2010没有问题,希望知道的能给点帮助。
关于虚基类:
虚基类
在说明其作用前先看一段代
{
public:
int iValue;
} ;
class B: public A
{
public:
void bPrintf(){cout<<"This is class B"<<endl;};
} ;
class C: public A
{
public:
void cPrintf(){cout<<"This is class C"<<endl;};
} ;
class D: public B, public C
{
public:
void dPrintf(){cout<<"This is class D"<<endl;};
} ;
void main()
{
D d;
cout<<d.iValue<<endl; //错误,不明确的访问
cout<<d.A::iValue<<endl; //正确
cout<<d.B::iValue<<endl; //正确
cout<<d.C::iValue<<endl; //正确
}
从代码中可以看出类B C都继承了类A的iValue成员,因此类B C都有一个成员变量iValue ,而类D又继承了B C,这样类D就有一个重名的成员 iValue(一个是从类B中继承过来的,一个是从类C中继承过来的).在主函数中调用d.iValue 因为类D有一个重名的成员iValue编译器不知道调用 从谁继承过来的iValue所以就产生的二义性的问题.正确的做法应该是加上作用域限定符 d.B::iValue 表示调用从B类继承过来的iValue。不过 类D的实例中就有多个iValue的实例,就会占用内存空间。所以C++中就引用了虚基类的概念,来解决这个问题。
{
public:
int iValue;
} ;
class B: virtual public A
{
public:
void bPrintf(){cout<<"This is class B"<<endl;};
} ;
class C: virtual public A
{
public:
void cPrintf(){cout<<"This is class C"<<endl;};
} ;
class D: public B, public C
{
public:
void dPrintf(){cout<<"This is class D"<<endl;};
} ;
void main()
{
D d;
cout<<d.iValue<<endl; //正确
}
在继承的类的前面加上virtual关键字表示被继承的类是一个虚基类,它的被继承成员在派生类中只保留一个实例。例如iValue这个成员,从类 D这个角度上来看,它是从类B与类C继承过来的,而类B C又是从类A继承过来的,但它们只保留一个副本。因此在主函数中调用d.iValue时就不 会产生错误。
虚基类总结:
1, 一个类可以在一个类族中既被用作虚基类,也被用作非虚基类。
2, 在派生类的对象中,同名的虚基类只产生一个虚基类子对象,而某个非虚基类产生各自的子对象。
3, 虚基类子对象是由最派生类的构造函数通过调用虚基类的构造函数进行初始化的。
4, 最派生类是指在继承结构中建立对象时所指定的类。
5, 派生类的构造函数的成员初始化列表中必须列出对虚基类构造函数的调用;如果未列出,则表示使用该虚基类的缺省构造函数。
6, 从虚基类直接或间接派生的派生类中的构造函数的成员初始化列表中都要列出对虚基类构造函数的调用。但只有用于建立对象的最派生 类的构造函数调用虚基类的构造函数,而该派生类的所有基类中列出的对虚基类的构造函数的调用在执行中被忽略,从而保证对虚基类子对象 只初始化一次。
7, 在一个成员初始化列表中同时出现对虚基类和非虚基类构造函数的调用时,虚基类的构造函数先于非虚基类的构造函数执行。
参考文献:
http://blog.163.com/xiangzaihui@126/blog/static/166955749201182295845689/(C++实现不能被继承的类)