目录
类的对象只能在堆上创建
类的对象只能在栈上创建
类只能创建一个对象(单例模式)
饿汉
懒汉
本篇博客就是说给一个需求,然后根据需求和类的一些语法规则设计出符合要求的类,比如说我们之前的不能拷贝的类:C++98的做法就是将拷贝构造仅声明且声明在私有,这样编译器不仅不会自动生成,外部也无法调用私有;C++11有关键字delete就可以直接解决,我们再来看一些其他的需求
类的对象只能在堆上创建
对于这种需求我们一般的思路就是封死掉大方向,然后给要求的方向开小门。就是说,我们把构造禁掉,然后单创建一个函数用来专门去堆上开辟空间,这个函数如果是普通成员函数,需要对象来调用,所以我们设置成静态成员函数。至于说拷贝构造和赋值运算符重载都要禁掉,我们就可以这么写
class HeapOnly { public:template<class...Args>static HeapOnly* Creatobj(Args&&...args){HeapOnly* tmp = new HeapOnly(forward<Args>(args)...);return tmp;}/*static HeapOnly* Creatobj(int a = 0, const char* str= "aaaaa", vector<double>&& vd = vector<double>()){HeapOnly* tmp = new HeapOnly(a,str,move(vd));return tmp;}*/HeapOnly(const HeapOnly& ho) = delete;//HeapOnly ho1(*pho1);HeapOnly& operator=(const HeapOnly& ho) = delete;//*pho1 = *pho2;void Destroyobj(){delete this;cout << "delete:" << this << endl;} private:HeapOnly(int a = 0, const char* str = "aaaaa", vector<double>&& vd = vector<double>()):_a(a),_str(str),_vd(vd){}int _a;string _str;vector<double> _vd; };
或者是我们把析构函数私有化,这样因为调不了析构,所以对象就无法在栈上生成,所以我们就可以去堆上申请(堆上申请的不会自动调用析构),然后创建destroy函数销毁对象
class HeapOnly { public:HeapOnly(int a = 0, const char* str = "aaaaa", vector<double>&& vd = vector<double>()):_a(a), _str(str), _vd(vd) {}void Destroyobj(){delete this;cout << "delete:" << this << endl;} private:~HeapOnly(){cout << "~HeapOnly()" << endl;}int _a;string _str;vector<double> _vd; };
我们可以这样使用
类的对象只能在栈上创建
这个和只能在堆上创建的大思路是一样的,我们构造私有化,然后单创建一个函数,这个函数就用来返回在栈上的对象,但是返回对象要用到拷贝构造,所以拷贝构造不能禁用。拷贝构造不禁用就可以new对象了,但是new会去调用operator new和(拷贝)构造,所以把operator new给封掉
class StackOnly { public:template<class...Args>static StackOnly Creatobj(Args&&...args){return StackOnly(forward<Args>(args)...);}StackOnly(const StackOnly&so):_a(so._a),_str(so._str){}void* operator new(size_t n) = delete;private:StackOnly(int a = 0, const char* str = "aaaa"):_a(a),_str(str){}int _a;string _str; };
类只能创建一个对象(单例模式)
有的类就要求只创建一个对象,比如内存池,我们每次申请内存都去同一个内存池;比如用来存配置信息的类,我们希望配置信息只有一份,那么这种类的设计方式有饿汉和懒汉两种方式,饿汉就是说在main函数之前这个对象就创建出来了,懒汉就是说等用到的时候才创建,我们下面就来介绍一下这两种方式
饿汉
因为只能创建一个对象,所以要将构造私有化,并且这个对象要创建成静态的,我们再创建一个函数,随时返回这个对象的指针,这样就可以保证要求了
namespace hunger {class Singleton{public:static Singleton* Getobj(){return &sin;}void print(){cout << _a << endl;for (auto& e : _vs)cout << e << ' ';cout << endl;}Singleton(const Singleton&) = delete;private:Singleton(int a = 0, vector<string>&& vs = {"aaaaaa","bbbbb"}):_a(a),_vs(vs){}int _a;vector<string> _vs;static Singleton sin;};Singleton Singleton::sin(111, { "abc","def" });};
这种创建方式也是有一些问题,如果单例对象数据较多,那么构造初始化这些对象的成本较高,影响程序的启动速度,迟迟进入不了main函数
如果两个单例分别位于两个文件中,它们两个需要有初始化的先后顺序,但是饿汉就无法控制先后顺序,因为很难说哪个文件先,哪个文件后
懒汉
就是说第一次使用的时候再去创建对象,我们也是创建一个静态成员对象,只不过这次是指针,因为通过判断指针是否为空就可以判断是否已经创建了对象
namespace lazy {class Singleton{public:static Singleton* Getpobj(){if (psin == nullptr){psin = new Singleton;}return psin;}static void destroyobj(){delete psin;}//内部类class GC{public:~GC(){Singleton::destroyobj();}};static GC gc;private:Singleton(int a = 0, vector<string>&& vs = { "aaaaaa","bbbbb" }):_a(a), _vs(vs) {}int _a;vector<string> _vs;static Singleton* psin;};Singleton* Singleton::psin = nullptr;Singleton::GC Singleton::gc; };
其实还有一种特别简单的方式,就是创建局部的静态对象,那么第一次调用函数就会初始化,后面都不会执行这句代码了
class Singleton{public:static Singleton* Getpobj(){static Singleton psin;return &psin;}private:Singleton(int a = 0, vector<string>&& vs = { "aaaaaa","bbbbb" }):_a(a), _vs(vs) {}int _a;vector<string> _vs;};