《剑指offer》:[48]不能被继承的类-单例模式
来源:互联网 发布:软件开发经费预算 编辑:程序博客网 时间:2024/06/05 15:35
题目:不能被继承的类
不能继承,一般我们会对构造函数做手脚。不能继承,继承会发生什么,继承的类在创建对象的时候,会自动调用父类的构造函数,如果我们在这里限制让子类不能调用父类的构造和析构就是实现了不能继承,但是也不能影响自己的使用。
方案一:思想:设置构造函数和析构函数为私有+新增两方法创建和销毁对象。
原因:
(1)把构造函数和析构函数设置为私有函数,这样可以防止子类调用构造函数和析构函数,这样也就实现了防止继承;
(2)新增两个方法来创建对象和销毁对象是为了不影响自己创建和销毁对象,不能为了限制别人把自己也坑了,这样就不划算了。所以我们采用了静态方法创建和销毁对象。
缺点:会影响类对象的创建,并且只能创建堆上的对象,不能创建栈上的对象(私有嘛)!
具体代码实现测试:
方法:虚基类+虚基类构造函数和析构函数私有化+不能派生的SealClass变成基类友员类
(1)另外设置一个基类Base,并把基类Base的构造函数和析构函数设置为私有。
(2)把要设置SealClass类变成基类的友元类
(3)SealClass类虚拟继承Base。
原因:
a、把基类Base的构造函数和析构函数设置为私有化,主要目的也是为了防止让其他类从基类base中派生,当然此时该类也不能定义对象;
b、设置SealClass类为基类Base的友元类,主要目的是根据友员的性质,该类可以自由访问基类的私有的构造函数和析构函数;
c、基类的友员不能被派生类继承,此时之后创建的Driver就不能使用父类的友元了。
d、为什么是虚函数?
主要原因:如果是虚继承的话,假设类Driver要从SealClass继承,由于是虚函数,该派生类Driver会直接调用虚基类Base的构造函数,而不会通过基类SealClass,此时就会报错。
虚继承还有一个优点:就是解决多继承的二义性问题,如果B和C都继承A,D继承B和C,如果D要用A的一个变量,就会出现二义性,是DBA还是DCA呢:
如果是虚继承,调用的时候就会越过BC,直接调用A的数据,解决了这种二义性的问题。
缺点:不易扩展,主要是因为编译器对虚基类和友元类friend类型的支持和要求不一样。
测试代码如下:
代码如下:
注意(2):在有虚继承的情况下, 派生类先调用虚基类的构造函数,再调用非虚基类的构造函数。虚基类的子对象在整个初始化过程中只调用一次。
不能继承,一般我们会对构造函数做手脚。不能继承,继承会发生什么,继承的类在创建对象的时候,会自动调用父类的构造函数,如果我们在这里限制让子类不能调用父类的构造和析构就是实现了不能继承,但是也不能影响自己的使用。
方案一:思想:设置构造函数和析构函数为私有+新增两方法创建和销毁对象。
原因:
(1)把构造函数和析构函数设置为私有函数,这样可以防止子类调用构造函数和析构函数,这样也就实现了防止继承;
(2)新增两个方法来创建对象和销毁对象是为了不影响自己创建和销毁对象,不能为了限制别人把自己也坑了,这样就不划算了。所以我们采用了静态方法创建和销毁对象。
缺点:会影响类对象的创建,并且只能创建堆上的对象,不能创建栈上的对象(私有嘛)!
具体代码实现测试:
#include <iostream>using namespace std;class SealClass{public:static SealClass *GetInstance()//<span style="font-family: Arial; font-size: 14px; line-height: 26px;">静态的全局访问接口;</span>{if(NULL==m_pInstance){m_pInstace = new SealClass();}return m_pInstace;}static void DeleteInstance(SealClass *pInstance){delete pInstance;}int GetNum(){return number;}private:static SealClass *m_pInstance; //<span style="font-family: Arial; font-size: 14px; line-height: 26px;">静态的私有实例化指针;</span>SealClass(){ }~SealClass() //其实析构函数可以不用写到这里;{cout<<"private: ~SealClass!"<<endl;}};class Driver:public SealClass{public:Driver(){ }};int main(){//SealClass ss(11);//ERROR,不能访问私有的构造函数;//SealClass *s=new SealClass(11);//ERROR,不能访问私有的构造函数;SealClass *ss=SealClass::GetInstance(11); //OK,只能是堆上的对象;int result=ss->GetNum();cout<<"number: "<<result<<endl;SealClass::DeleteInstance(ss);//创建子类://Driver d; //ERROR,不能访问构造和析构函数;//Driver *d=new Driver;//ERROR,不能访问构造和析构函数;system("pause");return 0;}
运行结果:
将函数及实例化指针设置为静态的主要有两点考虑:首先,类的静态成员变量就是指的类共享的对象,而单例模式的对象设成静态就是为了让该类所有成员共享同一个对象,所以从语义上是合适的;其次,从语法考虑,常见的单例模式都是通过一个静态方法(如getInstance)返回其单例,因为静态方法的内部不能直接使用非静态变量,所以返回的这个实例就是静态的。
方法:虚基类+虚基类构造函数和析构函数私有化+不能派生的SealClass变成基类友员类
(1)另外设置一个基类Base,并把基类Base的构造函数和析构函数设置为私有。
(2)把要设置SealClass类变成基类的友元类
(3)SealClass类虚拟继承Base。
原因:
a、把基类Base的构造函数和析构函数设置为私有化,主要目的也是为了防止让其他类从基类base中派生,当然此时该类也不能定义对象;
b、设置SealClass类为基类Base的友元类,主要目的是根据友员的性质,该类可以自由访问基类的私有的构造函数和析构函数;
c、基类的友员不能被派生类继承,此时之后创建的Driver就不能使用父类的友元了。
d、为什么是虚函数?
主要原因:如果是虚继承的话,假设类Driver要从SealClass继承,由于是虚函数,该派生类Driver会直接调用虚基类Base的构造函数,而不会通过基类SealClass,此时就会报错。
虚继承还有一个优点:就是解决多继承的二义性问题,如果B和C都继承A,D继承B和C,如果D要用A的一个变量,就会出现二义性,是DBA还是DCA呢:
如果是虚继承,调用的时候就会越过BC,直接调用A的数据,解决了这种二义性的问题。
缺点:不易扩展,主要是因为编译器对虚基类和友元类friend类型的支持和要求不一样。
测试代码如下:
#include <iostream>using namespace std;class Base{public:friend class SealClass;private:Base(){cout<<"private: Base!"<<endl;}~Base(){cout<<"private: ~Base!"<<endl;}};class SealClass:virtual public Base{public:SealClass(){cout<<"SealClass!"<<endl;}~SealClass(){cout<<"~SealClass!"<<endl;}};//class Driver:public SealClass//{//public://Driver()//{//cout<<"Driver!"<<endl;//}//~Driver()//{//cout<<"~Driver!"<<endl;//}//};int main(){//Driver d;//ERROR,不能访问私有的构造和析构://Driver *d=new Driver;//ERROR,不能访问私有的构造和析构:SealClass s; //OK;SealClass *ss=new SealClass; //OKdelete ss;system("pause");return 0;}运行结果:
由上得来的模板类,只要是某个类不想派生出子类,就可以使用此模板。
具体实现代码如下:
template<typename T>class Base{public:friend T;private:Base(){}~Base(){}};class SealClass : virtual public Base<SealClass>{public:SealClass(){}~SealClass(){}};注意(1):一定要设置为虚继承:如果不是虚继承的话,类Driver的对象会调用父类的构造函数,而且父类SealClass可以调用Base的构造函数和析构函数,则此时是可以有类从SealClass继承的。
代码如下:
#include <iostream>using namespace std;class Base{public:friend class SealClass;private:Base(){cout<<"private: Base!"<<endl;}~Base(){cout<<"private: ~Base!"<<endl;}};class SealClass: public Base{public:SealClass(){cout<<"SealClass!"<<endl;}~SealClass(){cout<<"~SealClass!"<<endl;}};class Driver:public SealClass{public:Driver(){cout<<"Driver!"<<endl;}~Driver(){cout<<"~Driver!"<<endl;}};int main(){Driver d;system("pause");return 0;}构造函数的顺序:
注意(2):在有虚继承的情况下, 派生类先调用虚基类的构造函数,再调用非虚基类的构造函数。虚基类的子对象在整个初始化过程中只调用一次。
例如:
#include <iostream>using namespace std;class Base{public:Base(){cout<<"private: Base!"<<endl;}~Base(){cout<<"private: ~Base!"<<endl;}};class SealClass: public Base{public:SealClass(){cout<<"SealClass!"<<endl;}~SealClass(){cout<<"~SealClass!"<<endl;}};class Driver:public SealClass{public:Driver(){cout<<"Driver!"<<endl;}~Driver(){cout<<"~Driver!"<<endl;}};int main(){Driver d;system("pause");return 0;}运行结果:
2 0
- 《剑指offer》:[48]不能被继承的类-单例模式
- [剑指offer][面试题48]不能被继承的类
- 剑指offer 48-不能被继承的类
- 【面试题】剑指Offer-48-不能被继承的类
- 剑指offer 面试题48 不能被继承的类
- 【剑指offer】题48:不能被继承的类
- 单件模式和不能被继承的类
- 剑指offer 6.5 发散思维 - 不能被继承的类
- 剑指offer——不能被继承的类
- 剑指Offer——不能被继承的类
- 剑指offer面试题目:不能被继承的类
- 剑指offer 48---设计一个类不能被继承
- 剑指offer面试题48C++写一个不能被继承的类
- 剑指offer面试题48 C++写一个不能被继承的类
- 《剑指Offer》学习笔记--面试题48:不能被继承的类
- 【剑指offer】6.5发散思维能力——面试题48:不能被继承的类
- 【剑指 offer】(48)—— 不能被继承的类
- 剑指offer——面试题48:用C++设计一个不能被继承的类
- 属性src和background区别
- 用文件保存学生信息
- swipe轮播插件,支持手势,简单实用
- 关于结婚
- Emacs入门笔记
- 《剑指offer》:[48]不能被继承的类-单例模式
- 使用 ResultSet 遍历查询结果String getString(int columnIndex)
- 翻转课堂心得
- bzoj1191(最大匹配,但不完全一样)
- <JAVA学习笔记6>——程序死锁、中断
- nginx之keepalive请求长连接复用率统计
- C语言基础 swtich结构中变量是字符,case的形式
- 观察者模式下的搜索引擎结合浏览器的完美实现
- JZOJ2756. 【SDOI2012】Longge的问题