C++特殊类设计
温馨提示:这篇文章已超过371天没有更新,请注意相关的内容是否还可用!
目录
一. 设计一个类,不能被拷贝
二. 设计一个类,使其只能在堆上创建
2.1 方式一
2.2 方式二
2.3 方式三
三. 设计一个类,使其只能在栈上创建
四. 单例模式(设计一个类,只能创建一个对象))
4.1 设计模式
4.2 单例模式
4.2.1 饿汉模式
4.2.2 懒汉模式
一. 设计一个类,不能被拷贝
拷贝只会放生在两个场景中:拷贝构造函数以及赋值运算符重载,因此想要让一个类禁止拷贝, 只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。
- C++98
在C++98中,我们需要将拷贝构造函数以及赋值运算符重载函数设为私有,并且只声明,不实现。
class CopyBan { // ... private: CopyBan(const CopyBan&); CopyBan& operator=(const CopyBan&); //... };原因:
(1)设置成私有:如果只声明没有设置成private,用户自己如果在类外定义了,就可以不 能禁止拷贝了
(2)只声明不定义:不定义是因为该函数根本不会调用,定义了其实也没有什么意义,不写 反而还简单,而且如果定义了就不会防止成员函数内部拷贝了。
- C++11
C++11扩展delete的用法,delete除了释放new申请的资源外,如果在默认成员函数后跟上
=delete,表示让编译器删除掉该默认成员函数。
class CopyBan { // ... CopyBan(const CopyBan&)=delete; CopyBan& operator=(const CopyBan&)=delete; //... };二. 设计一个类,使其只能在堆上创建
2.1 方式一
只能在堆上创建,那么我们就只能通过申请资源的方式创建对象。
首先我们要将构造函数私有化,拷贝构造以及赋值拷贝禁掉,来防止通过栈上的对象拷贝赋值或者直接在栈上创建对象来创建对象。在我们禁掉构造函数以及拷贝构造之后,可以不考虑赋值拷贝的问题,因为已经不可能通过直接构造或者拷贝来创建栈上对象了,而创建出的对象一定是CreateOnly函数创建的,即一定是堆上的,而赋值拷贝是针对已经有的对象,所以可以不考虑。
那我们就要通过自己实现的CreateOnly函数来调用构造函数创建对象,并且将此函数加上static修饰,因为我们将构造函数私有化之后,是不能创建出对象的,就不能通过对象去调用CreateOnly函数,因此要加上static,这样直接用类域调用就行。
//只能在堆上 class Heaponly { public: static Heaponly* CreateOnly()//静态是因为不能通过构造函数构造对象,所以不能对函数进行引用 { return new Heaponly; } Heaponly(const Heaponly& ho) = delete; Heaponly& operator=(const Heaponly& ho) = delete; private: Heaponly() {} int _x; int _y; vector _a; }; int main() { //Heaponly h; Heaponly* hp = Heaponly::CreateOnly(); return 0; }2.2 方式二
如果有多种构造函数,则可以用可变参数模板:
//只能在堆上 class Heaponly { public: template static Heaponly* CreateOnly(Args... args)//静态是因为不能通过构造函数构造对象,所以不能对函数进行引用 { return new Heaponly(args...); } Heaponly(const Heaponly& ho) = delete;//防止通过栈上的对象拷贝构造 Heaponly& operator=(const Heaponly& ho) = delete;//防止通过栈上的对象赋值拷贝 private: //构造私有化 Heaponly() {} Heaponly(int x,int y) :_x(x) ,_y(y) {} int _x; int _y; vector _a; }; int main() { //Heaponly h; //Heaponly* hp1 = new Heaponly; //Heaponly* hp = Heaponly::CreateOnly(); Heaponly* hp1 = Heaponly::CreateOnly(1, 1); Heaponly* hp2(hp1); return 0; }2.3 方式三
我们还可以通过把析构函数私有化来实现只能在堆上创建。
这样只要是栈上创建的对象都会编译报错,因为无法调用其析构函数,但是指针却可以正常的开辟空间,那我们要如何释放空间呢,可以自己定义一个销毁函数来调用析构函数销毁对象。
//只能在堆上创建还可以封掉析构函数 class Heaponly { public: Heaponly() {} Heaponly(int x,int y) :_x(x) ,_y(y) {} void Destory() { delete this; } private: ~Heaponly() { cout Destory(); }); return 0; }三. 设计一个类,使其只能在栈上创建
只在栈上创建对象,也就是要禁止申请资源空间。就需要变换一下CreateOnly函数,通过调用拷贝构造来构造对象。
并且还要禁掉new,在C++中,new是由operator new和构造函数组成的,那我们就得重载一个operator new函数,并且禁掉。
operator new
- 是C++中用于动态分配内存的内置运算符
- 主要作用是分配一块连续的内存空间,以便在其中存储对象或数据
- 可以被重载
//只能在栈上 class Stackonly { public: template static Stackonly CreateOnly(Args... args)//静态是因为不能通过构造函数构造对象,所以不能对函数进行引用 { return Stackonly(args...); } Stackonly(const Stackonly& ho) = delete; Stackonly& operator=(const Stackonly& ho) = delete; //重载一个类专属的operator new void* operator new(size_t n) = delete; private: //构造私有化 Stackonly() {} Stackonly(int x,int y) :_x(x) ,_y(y) {} int _x; int _y; vector _a; }; int main() { Stackonly so1 = Stackonly::CreateOnly(); Stackonly so2 = Stackonly::CreateOnly(1, 1); //如果是new的话就避免不了,就要封掉operator new //Stackonly* so3 = new Stackonly(so1); return 0; }也可以跟上面一样,把析构函数私有化,这样申请到的资源无法释放,也就不能在堆上创建对象。
那么类似,delete跟new一样,是由operator delete和析构函数组成的。那么我们也可以重载一个operator delete函数并且禁掉。
operator delete
- 和operator new配套的运算符,用于释放动态分配内存的内置运算符
- 通过传递要释放的内存块的指针,它将该内存块返回给系统或内存管理器,以便将其重新分配给其他用途
- 允许重载
void* operator delete(size_t n) = delete;
四. 单例模式(设计一个类,只能创建一个对象))
4.1 设计模式
设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。
设计模式目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 设计模式使代码编写真正工程化。
4.2 单例模式
一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。
4.2.1 饿汉模式
在饿汉模式中,单例实例是在main函数之前就被创建。
因为只能有一个对象,所以拷贝构造函数以及赋值拷贝函数都要禁用,构造函数需要私有。
并且要实现一个接口函数来返回一个对象,并且这个对象必须是static,因为返回的必须是全局的,而不是局部的。
接口函数也要设计为static的,因为构造函数私有,不能通过对象调用,只能用类域调用。
由于是预先实例化,所以在类中实例化就行。
namespace hunger { //饿汉:从一开始(main之前)就创建对象 //问题: //1、如果单例对象数据较多,构造初始化成本较高,那么会影响程序启动的速度,迟迟进不了main函数 //2、多个单例类对象有初始化启动依赖关系,饿汉无法控制。假设:A和B两个单例,假设要求A先初始化,B后初始化,饿汉满足不了 class Singleton { public: static Singleton* GetInstance() { return &_sint; } void Print() { cout
- C++11





