目录
本篇博客就是说给一个需求,然后根据需求和类的一些语法规则设计出符合要求的类,比如说我们之前的不能拷贝的类: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; };