C++虚拟继承
来源:互联网 发布:移动4g网络怎么开通 编辑:程序博客网 时间:2024/06/06 19:07
C++虚拟继承
这篇文章主要讨论了C++中有关“不能被继承的类”,并对虚拟继承进行了分析。
注意:下面的代码在VC2003上编译过,但在VC2005Beta版上会有问题。我想可能是试用版上有bug吧。
引出问题:
前天,一个同事给我看了一段代码,问我这段代码的涵义。乍一看我没有看出明堂。后来在与几个同事一起深入研究后发现了它的奥妙。这其中涉及到一些C++中的高级技术,很有意思。我把我们的分析作了一个总结,借这块宝地,拿出来供大家共同学习。原始代码如下:
//file name: NoInherit.h
template <class T> class NoInherit_;
template <class T,template<class > class A >
class NoInherit_< A<T> >
{
private:
friend A<T>;
friend T;
NoInherit_(){}
};
template <class T>
class NoInherit:virtual public NoInherit_< NoInherit< T> >
{
public:
typedef T type;
private:
friend T;
NoInherit():NoInherit_< NoInherit< T> >()
{}
};
如果你是大虾一下子就看明白了这段代码,那么这篇文章对你就没有多大价值了,兄弟就不敢再耽搁你的时间了。如果你不能一下子看出来,那就让我来告诉你吧。它是一个不能被其它类继承的类模板。不信你试试……
按照下面的方法使用这个模板:
class CA:NoInherit<CA>
{
//……
};
这个类CA不能再被其它类继承了.像这样
Class CB:CA
{
}
//error……编译会出错。提示不能访问NoInherit_的私有构造函数。
那么这个类模板是怎么样达到“不让其它类继承”的目的的呢?也许你已经看出来了。它是通过私有构造函数和虚拟继承来实现的。在我分析这个模板的过程中我思考过下面这几个问题:
1、 有哪些方法能实现“不让其它类继承”呢?
2、 为什么要用到friend A<T>;
3、 虚拟继承是怎么一回事?为什么在这里要使用虚拟继承呢?
4、 NoInherit_中friend A<T>好理解,friend T怎么理解?
下面我就这几个问题展开讨论。
有哪些方法实现“不让其它类继承”
我们知道,子类继承父类,肯定会做的事情是:调用父类的构造函数(注意:子类不一定会调用父类的析构函数。为什么?自己想去吧!)。我们只要父类的构造函数是private的,子类想继承,没门儿!
“不让其它类继承”就这么简单。是的,就这么简单!
看官一定会问,既然这么简单,上面的代码为什么那么复杂呢?别急别急,待我下面慢慢道来……
我们先分析下面几种情况:
A)单件(single)模式
//single.h
class CSingle
{
public:
static CSingle *CreateInstance() ;
static void DeleteInstance() ;
……
private:
static CSingle *m_pSingle;
Csingle(){};
Csingle(Csingle&){};
};
//single.cpp
#include “single.h”
CSingle * CSingle ::m_pSingle = NULL;
CSingle * CSingle ::CreateInstance()
{
If(!m_pSingle)
m_pSingle = new Csingle;
return m_pSingle;
}
CSingle :: DeleteInstance ()
{
delete m_pSingle;
:m_pSingle = NULL;
}
它能够实现“不让其它类继承”,但很显然这种方式不好,它只能有一个实例。那么下面这样呢?
class CExample
{
public:
CExample* CreateInstance()
{
return new CExample;
}
private:
CExample();
CExample(CExample&);
};
网上很多人提到这种方法。我个人也认为这个方法可行。但是这不是最理想的方法,因为这种方法限制了CExample对象的创建:new CExample 和CExample example。
B)http://springsun.bokee.com/218785.html上提供了下面这种方法。
// Usable.h
class Usable;
class Usable_lock
{
friend class Usable;
private:
Usable_lock() {}
Usable_lock(const Usable_lock&) {}
};
class Usable : public virtual Usable_lock
{
// ...
public:
Usable();
Usable(char*);//这是为什么没有明白。
// ...
};
Usable a;
class DD : public Usable { };
DD dd; // error: DD::DD() cannot access
// Usable_lock::Usable_lock(): private member
//(参见《The Design and Evolution of C++》,11.4.3节)
这跟上面一开始给出的代码原理是一样的。只不过上面将其模板化了。我个人推荐这里给出的方法。
下面的讨论,如果你看最上面的代码费劲,看这里的代码好了。我也将以这里的代码为例进行说明。
为什么需要friend class
当创建Usable对象时需要调用Usable_lock的构造函数,而Usable_lock的构造函数是私有的,所以friend class Usable;是必要的。
虚拟继承
说老实话,我以前做项目没有使用过这个东西。这次正好有机会,仔细研究了一下。
从《C++程序设计语言(特别版)》和《c++_primer》中可以知道虚拟继承是一项专为解决多继承中的一个问题的。看下面的代码:
//非虚拟继承
class Base
{
public:
Base()
{
cout<<"Base construct"<<endl;
}
};
class CA:public Base
{
public:
CA()
{
cout<<"CA construct"<<endl;
}
};
class CB:public Base
{
public:
CB()
{
cout<<"CB construct"<<endl;
}
};
class CC:public CA,public CB
{
public:
CC()
{
cout<<"CC construct"<<endl;
}
};
int main()
{
CC tt;
getchar();
return 0;
}
//虚拟继承
class Base
{
public:
Base()
{
cout<<"Base construct"<<endl;
}
};
class CA: virtualpublic Base
{
public:
CA()
{
cout<<"CA construct"<<endl;
}
};
class CB: virtualpublic Base
{
public:
CB()
{
cout<<"CB construct"<<endl;
}
};
class CC:public CA,public CB
{
public:
CC()
{
cout<<"CC construct"<<endl;
}
};
int main()
{
CC tt;
getchar();
return 0;
}
运行结果如下:
Base construct
CA construct
Base construct
CB construct
CC construct
运行结果如下:
Base construct
CA construct
CB construct
CC construct
后面一段程序使用了虚拟继承,运行时少调了一次Base construct。为什么呢?
四个类的继承关系如下:
没有虚拟继承的情况下,多重继承下的对象可能的布局:
CC对象有两个Base子对象。
在虚拟继承的情况下,多重继承下的对象的可能布局:
CA和CB子对象只保留了一个对Base子对象的引用.
(这两幅图参考自《C++程序设计陷阱》)
也就是说:在虚拟继承的情况下由最底层的派生类的构造函数初始化虚基类。
在非虚拟继承中,子类只能初始化它的直接父类,而在虚拟继承中最底层的子类可以初始化虚基类。比如:
class Base
{
public:
Base(char * pName = "Base")
{
strName = pName;
cout<<"Base construct"<<endl;
cout<<strName.data()<<endl;
}
private:
string strName;
};
class CA:virtual public Base
{
public:
CA():Base("CA")
{
cout<<"CA construct"<<endl;
}
};
class CB:virtual public Base
{
public:
CB():Base("CB")
{
cout<<"CB construct"<<endl;
}
};
class CC:public CA,public CB
{
public:
CC():Base("CC")
{
cout<<"CC construct"<<endl;
}
};
我们可以这样理解:在非虚拟继承中,创建孙子类对象时,孙类对象先构造它的父类,父类又先构造父类的父类,这样一层一层向上追溯;在虚拟继承中,孙子类对象是直接调用了虚基类的构造函数,再处理非虚拟父类的(详细解释可以参考《C++程序设计陷阱》)。
到这里我们就能解释为什么上面提到的http://springsun.bokee.com/218785.html中的代码要使用虚拟继承了。如果不使用虚拟继承,那么class DD构造时就先调用class Usable_lock的构造函数。因为class Usable_lock是class Usable的友元类,所以它能够调用class Usable的构造函数了。这样就能实现class DD继承于class Usable_lock了。如果使用的是虚拟继承,那么class DD构造时就先调用class Usable的构造函数,显然这是不行的。因为class Usable的构造函数是私有的,而class DD又不是它的友元类。这样编译器就会提示不能访问class Usable的私有构造函数了。
到这里我们也就能理解最开始给出的那个模板类了。
NoInherit_中friend T怎么理解
我们象下面这样来使用这个模板:
class CTest :publicNoInherit< CTest >
{
//……
}
这显然是行得通的。因为有了friend T,CTest就成了NoInherit和NoInherit_的友元,从而能够初始化这两个类。
如果我们这样:
class CMyClass:public CTest //error
{
}
就会出错。因为创建CMyClass对象时不能够造NoInherit和NoInherit_。从而CTest就是一个不能被人继承的类了。我们自己的工作就在CmyClass中完成。
关于模板特化:
http://blog.csdn.net/SmartPtr/archive/2007/06/10/1647144.aspx
- c++-虚拟继承、虚基类
- C++-继承:多重继承 && 虚拟继承
- 【c++】菱形虚拟虚拟继承模型探索
- C++:菱形继承和虚拟继承
- C/C++——虚拟继承
- 【C++】菱形虚拟继承(内存布局)
- C++: 虚表和菱形虚拟继承
- (虚拟继承)Problem C: 学生干部虚基类
- 【C++】菱形继承与虚拟菱形继承的对比分析
- 虚拟继承
- 虚拟继承
- 虚拟继承
- 虚拟继承
- 虚拟继承
- 虚拟继承
- 虚拟继承
- 虚拟继承
- 虚拟继承
- 解决Extjs4 Store load方法传中文参数出现乱码问题
- 【算法导论】动态规划之“最优二叉搜索树”
- iOS 异常处理,将bug信息发送到开发者邮箱
- 和大神们学习每天一题(leetcode)-Excel Sheet Column Title
- UVALive 6650 Falling Ants
- C++虚拟继承
- 系统信息书包SYS
- Linux 汇编器:对比 GAS 和 NASM
- 高进口坚果洪湖里可
- EditText的常用属性
- JS判断输入日期的正确性
- android JNI 使用的两种形式 --自己实现c和调用第三方so库
- Android中ViewFlipper的使用
- makefile文件中dash include的含义