文章目录
- 一、设计一个类,不能被拷贝
- 1.C++98方法
- 2.C++11方法
- 二、设计一个类,只能在堆上创建对象
- 1.析构函数私有化
- 2.构造函数私有化
- 三、请设计一个类,只能在栈上创建对象
- 四、设计一个类不能被继承
- 1.C++98方式
- 2.C++11方式
- 五、设计一个类,只能创建一个对象(单例模式)
- 1.饿汉模式
- 2.懒汉模式
一、设计一个类,不能被拷贝
这个我们前面已经说过了
有两种方法
1.C++98方法
将拷贝构造函数与赋值运算符重载只声明不定义,并且将其访问权限设置为私有即可。
class CopyBan
{// ...private:CopyBan(const CopyBan&);CopyBan& operator=(const CopyBan&);//...
};
原因:
-
设置成私有:如果只声明没有设置成private,用户自己如果在类外定义了,就可以不能禁止拷贝了
-
只声明不定义:不定义是因为该函数根本不会调用,定义了其实也没有什么意义,不写反而还简单,而且如果定义了就不会防止成员函数内部拷贝了。
2.C++11方法
C++11扩展delete的用法,delete除了释放new申请的资源外,如果在默认成员函数后跟上=delete,表示让编译器删除掉该默认成员函数。
class CopyBan
{// ...CopyBan(const CopyBan&)=delete;CopyBan& operator=(const CopyBan&)=delete;//...
};
二、设计一个类,只能在堆上创建对象
1.析构函数私有化
如下所示,由于在栈区和静态区的资源在生命周期结束的时候会调用析构函数。所以我们大可以直接将析构函数私有化。
这样一来,栈区和静态区的就无法创建对象了。而对于堆区的,由于他本身不会自动调用析构函数。我们需要手动去释放,但是由于我们现在的析构函数私有化了,所以我们可以通过一个接口去析构。如下代码所示:
class HeapOnly
{
public:void Destory(){delete this;}
private:~HeapOnly(){//...}
};
int main()
{HeapOnly hp1;static HeapOnly hp2;HeapOnly* hp3 = new HeapOnly;hp3->Destory();return 0;
}
我们可以明显的注意到,前两个是报错的
2.构造函数私有化
这里需要注意的是,我们也要封住拷贝构造函数。因为可能会通过拷贝构造函数去创建栈区上的对象
class HeapOnly
{
public:static HeapOnly* CreatObj(){return new HeapOnly;}
private:HeapOnly(){//...}//C++11的方法,拷贝构造必须封HeapOnly(const HeapOnly& hp) = delete;//赋值运算符重载可封可不封HeapOnly& operator=(const HeapOnly& hp) = delete;
};
int main()
{HeapOnly hp1;static HeapOnly hp2;HeapOnly* hp3 = HeapOnly::CreatObj();//封住拷贝构造是为了防止下面的情形HeapOnly hp4(*hp3);return 0;
}
我们显然看到,我们这个只能在堆区创建了
三、请设计一个类,只能在栈上创建对象
如下代码所示
为了让只在栈上创建对象,我们肯定不可以封住析构函数,因为栈区的一定会调用析构函数。
所以我们只能从构造函数下手,于是我们可以将构造函数私有化,然后提供一个接口去接收这个对象。
这里还需要注意的是,我们的new也可以是拷贝构造。但是我们是不可以封住拷贝构造的,因为我们返回一个对象的时候,需要调用拷贝构造。
我们注意到operator new是在全局中的一个函数重载,所以我们可以利用它会优先访问类域的特性,我们在类里面实现一个专属的operator new,然后我们就可以将这个函数给删掉。也就是无法使用new了。
最终我们就彻底屏蔽了堆区的创建。
但是这里我们其实还有一个静态区如果调用拷贝构造怎么办?这里如果还有屏蔽掉静态就比较麻烦了。虽然无法彻底解决问题,但是也已经可以了。
class StackOnly
{
public:static StackOnly CreatObj(){return StackOnly();}
private:StackOnly(){//...}//对一个类实现专属的operator newvoid* operator new(size_t size) = delete;
};int main()
{StackOnly st1;static StackOnly st2;StackOnly* st3 = new StackOnly;StackOnly st4 = StackOnly::CreatObj();StackOnly st5(st4);StackOnly* st6 = new StackOnly(st4);return 0;
}
我们可以注意到,确实只可以在栈上创建对象
四、设计一个类不能被继承
1.C++98方式
由于继承的派生类必须调用基类的构造函数。所以我们可以封住构造函数
// C++98中构造函数私有化,派生类中调不到基类的构造函数。则无法继承
class NonInherit
{
public:static NonInherit GetInstance(){return NonInherit();}
private:NonInherit(){}
};
2.C++11方式
final关键字可以禁止某个虚函数无法被重写
final还可以修饰类,表示该类不能被继承。
class A final
{// ....
};
五、设计一个类,只能创建一个对象(单例模式)
1.饿汉模式
饿汉模式就是:一开始(main函数之前)就创建单例对象
问题:
- 如果单例对象很大或者初始化内容很多,影响启动速度
- 如果两个单例类,互相有依赖关系。要求A先创建,B再创建,B的初始化创建依赖A
饿汉模式的实现如下
要注意,为了防止拷贝构造或者赋值运算符重载去创建新的对象,要将他们给封住
class Singleyton
{
public:static Singleyton* GetInstance(){return &_sinst;}
private:Singleyton(){}//禁止拷贝Singleyton(const Singleyton& s) = delete;//禁止赋值Singleyton& operator=(const Singleyton& s) = delete;map<string, string> _dict;//可以放到静态区,注意这里只是声明static Singleyton _sinst;
};
//定义,这里的_sinst是在类里面声明的,所以可以调用类里面的构造函数
//就像一个函数在类里面声明,在外面定义是可以直接使用类里面的成员变量一样的
Singleyton Singleyton::_sinst;int main()
{Singleyton* s1 = Singleyton::GetInstance();Singleyton* s2 = Singleyton::GetInstance();Singleyton* s3 = Singleyton::GetInstance();Singleyton* s4 = Singleyton::GetInstance();cout << s1 << endl;cout << s2 << endl;cout << s3 << endl;cout << s4 << endl;return 0;
}
2.懒汉模式
我们先看下面的代码
懒汉模式其实就是一开始先不创建创建对象,而是在第一次去获取这个对象的时候去创建的。
对于单例模式它一般是不需要释放的。但是在一些特殊场景还是需要的。这时候我们需要显示释放,程序结束时,需要做一些持久化的动作(写到文件中去)
如下中,是写了一个Delnstance函数去释放这块资源的。
为了方便,我们可以专门去写一个类,定义一个全局对象去专门来释放它。
namespace lazy
{class Singleyton{public:static Singleyton* GetInstance(){//如果还没有创建,就创建一下这个对象if (_psinst == nullptr){_psinst = new Singleyton;}return _psinst;}//一般而言单例不用释放,//在一些特殊场景: 1. 需要显示释放, 2. 程序结束时,需要做一些特殊动作(如持久化)static void DelInstance(){if (_psinst){delete _psinst;_psinst = nullptr;}}~Singleyton() {cout << "~Singleyton()" << endl;//map的数据写到文件中FILE* fin = fopen("map.txt", "w");for (auto& e : _dict){fputs(e.first.c_str(), fin);fputs(":", fin);fputs(e.second.c_str(), fin);}}void ADD(string s1, string s2){_dict[s1] = s2;}void Print(){for (auto& e : _dict){cout << e.first << ":" << e.second << endl;}}private:Singleyton(){}//禁止拷贝Singleyton(const Singleyton& s) = delete;//禁止赋值Singleyton& operator=(const Singleyton& s) = delete;map<string, string> _dict;//可以放到静态区,注意这里只是声明static Singleyton* _psinst;};Singleyton* Singleyton::_psinst = nullptr;
}
class GC
{
public:~GC(){lazy::Singleyton::DelInstance();}
};
GC g;
int main()
{lazy::Singleyton* s1 = lazy::Singleyton::GetInstance();s1->ADD("xxx", "111");s1->ADD("yyy", "222");s1->ADD("zzz", "333");s1->ADD("abc", "444");s1->Print();//s1->DelInstance();return 0;
}
运行结果为
不过我们也可以将这个类写到内部类里面,这样的话这个也是可以的
namespace lazy
{class Singleyton{public:class GC{public:~GC(){lazy::Singleyton::DelInstance();}};static Singleyton* GetInstance(){//如果还没有创建,就创建一下这个对象if (_psinst == nullptr){_psinst = new Singleyton;}return _psinst;}//一般而言单例不用释放,//在一些特殊场景: 1. 需要显示释放, 2. 程序结束时,需要做一些特殊动作(如持久化)static void DelInstance(){if (_psinst){delete _psinst;_psinst = nullptr;}}~Singleyton() {cout << "~Singleyton()" << endl;//map的数据写到文件中FILE* fin = fopen("map.txt", "w");for (auto& e : _dict){fputs(e.first.c_str(), fin);fputs(":", fin);fputs(e.second.c_str(), fin);}}void ADD(string s1, string s2){_dict[s1] = s2;}void Print(){for (auto& e : _dict){cout << e.first << ":" << e.second << endl;}}private:Singleyton(){}//禁止拷贝Singleyton(const Singleyton& s) = delete;//禁止赋值Singleyton& operator=(const Singleyton& s) = delete;map<string, string> _dict;//可以放到静态区,注意这里只是声明static Singleyton* _psinst;static GC _gc;};Singleyton* Singleyton::_psinst = nullptr;Singleyton::GC Singleyton::_gc;
}int main()
{lazy::Singleyton* s1 = lazy::Singleyton::GetInstance();s1->ADD("xxx", "111");s1->ADD("yyy", "222");s1->ADD("zzz", "333");s1->ADD("abc", "444");s1->Print();//s1->DelInstance();return 0;
}