那些奇葩的类的设计

1.不能被拷贝的类

一个类不能被拷贝,说明,不能利用已经存在的对象去创建新的对象,要想实现这一点,只需要让拷贝构造函数失效即可。

C++98的方式:将构造函数私有化,并且,只声明,不定义;如果只是不实现,没有将构造函数私有化,防不住有人在类外定义;由于构造函数不能使用,所以也就没有实现的必要了。如下代码:

// 设计一个不能被拷贝的类
class NoCopy
{
public:
	NoCopy(){}
	
private:
	NoCopy(const NoCopy& obj);

	int _x;
	int _y;
	string _str;
};

C++11的方式:C++11提供一个关键字delete,用来禁止生成指定的默认成员函数。

class NoCopy
{
public:
	NoCopy()
	{}
	NoCopy(const NoCopy& obj) = delete;
private:
	int _x;
	int _y;
	string _str;
};

2.只能在堆上创建的类

方式一:在堆上创建一个对象,我们只能通过malloc之类的函数,以及new操作符来完成;如果一个类只能在堆上创建,说明只能由我们在堆上申请,那么我们就需要在该类中提供一个对外的接口,通过该接口在堆上创建对象。 那么这个类原来的创建对象的方式就都不能对外提供了,但是也不能禁用,因为new的底层会做两件事情 1.开辟空间;2.调用构造函数完成对象的初始化。所以构造函数需要设置成私有。但是一个类的构造函数可能不止一个,可能会有不同的参数,所以我们可以利用 模版的可变参数 以及 万能引用 来解决这个问题。具体实现可以参考下面这份代码:

// 只能在堆上创建对象
class OnlyHeapCreate
{
public:
	template<class... Args>
	static OnlyHeapCreate* CreateObj(Args&&... args)
	{
		return new OnlyHeapCreate(args...);
	}
    // 需要自己实现以下两个函数,我就不实现了~
	OnlyHeapCreate(const OnlyHeapCreate&) = delete;
	OnlyHeapCreate& operator=(const OnlyHeapCreate&) = delete;
private:
	// 构造私有化
	OnlyHeapCreate(){}

	OnlyHeapCreate(int x, int y)
		:_x(x)
		,_y(y)
	{}

	int _x;
	int _y;
	vector<int> _v;
};

方式二:将析构函数私有化,在栈上申请的对象销毁的时候,需要调用析构函数清理资源,会发现无法调用私有的析构函数, 所以我们需要提供对外的清理资源的接口,将该接口实现成delete 形式,倒逼该类只能在堆上创建。实现方式如下:

class OnlyHeapCreate
{
public:
	OnlyHeapCreate() {}

	OnlyHeapCreate(int x, int y)
	:_x(x),_y(y)
	{}

	void Destroy() {delete this;}

private:
	// 封掉析构函数
	~OnlyHeapCreate() {cout << "~OnlyHeapCreate()" << endl;}

	int _x;
	int _y;
	vector<int> _v;
};

3.只能在栈上创建的类

一个类要想只能在栈上创建对象,那么他就不能通过new在堆上申请空间,所以,我们只需要让new操作符失效即可;如何让new操作符失效呢?new的底层会去调用operator new(),我们没办法直接让new失效,但是我们可以让operator new() 失效,从而间接让new操作符失效,所以该类只能在栈上创建对象了。new的底层还会去调用构造函数初始化对象,但是我们并不清楚会调用哪个构造函数,所以,所以我们可以利用 模版的可变参数 以及 万能引用 来解决这个问题。示例代码如下:

// 只能在Stack
class OnlyStackCreate
{
public:
	template<class... Args>
	static OnlyStackCreate CreateObj(Args&&... args)
	{
		return OnlyStackCreate(args...);
	}

	OnlyStackCreate& operator=(const OnlyStackCreate&) = delete;

	// 重载一个类专属的operator new
	void* operator new(size_t n) = delete;

private:
	// 构造私有化
	OnlyStackCreate() {}

	OnlyStackCreate(int x, int y)
		:_x(x)
		,_y(y)
	{}

	int _x;
	int _y;
	vector<int> _v;
};

4.不能被继承的类

C++98的方式:将父类的构造函数私有化;因为,子类会继承父类中的成员,创建子类对象时,子类对象中继承自父类的成员需要调用父类的构造函数初始化,如果父类的构造函数是私有的,子类就无法调用,因此,该类就不能被继承。实现方式如下:

class A
{
private:
	A() {}
};

C++11的方式:C++11提供了一个final关键字,如果一个类不想被继承,可以使用final关键字表明该类不能被继承。 

class A final
{};

5.只能创建一个对象的类

有些类要求只能创建一个对象,在设计模式中,这样的类被称为单例模式;具体来说,单例模式下,一个类只能创建一个对象,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。

实现单例模式有两种方法,分别是 懒汉模式饿汉模式。(什么是设计模式?林子大了,什么鸟都有,IT界也是一样,对于相同的问题,总有不同的解决方案,方案多了,就有好的有坏的,将好的方案总结出来,大家都遵守,就形成了设计模式)

为什么需要单例呢?对于程序中一些只需要一份的数据,就可以把这些数据放在一个类里面,再把这个类设计成单例,那么这些数据就只有一份了。简单来说,单例是用来管理程序中只需要一份的数据。

饿汉模式

什么是饿汉模式呢?就好比人吃饭,如果一个人很饿,他肯定希望去吃饭的时候就有干净的碗,直接拿来用就可以了;这里的碗就相当于数据,这个人就相当于程序;所以饿汉模式下,数据的加载在程序运行之前。饿汉模式代码如下:

class Single
	{
	public:
		static Single* GetInstance()
		{
			return &_sin;
		}

		void AddStr(const string& s) {_vstr.push_back(s);}

		Single(Single const&) = delete;
		Single& operator=(Single const&) = delete;

	private:
		Single(int x = 0, int y = 0, const vector<string>& vstr = { "hello","world" })
			:_x(x)
			, _y(y)
			, _vstr(vstr)
		{}

		int _x;
		int _y;
		vector<string> _vstr;

		static Single _sin;
	};

	Single Single::_sin(1, 1, { "大佬","你好" });
  • 整个单例中,提供一个静态的相同的类类型的指针,对该类中所有成员的操作都通过该指针来操作,提供一个对外的接口GetInstance()用来获取该指针;由于该指针被static修饰,虽然定义在类中,受到类域的限制,但是它依旧是一个静态的全局变量;
  • 对于静态全局变量,如果其初始化表达式是编译时常量或者只涉及其他静态变量(不调用函数),则它们通常在程序启动时的一个早于main函数执行的阶段被初始化。这个初始化过程发生在程序的所有动态初始化之前,以及任何线程开始执行之前。
  • 在程序执行之前,单例中的数据都准备好了,所以这就是饿汉模式。

饿汉的缺陷

  • 如果单例对象数据较多,构造初始化成本较高,那么会影响程序启动的速度。迟迟进不了main函数
  • 如果多个单例类有初始化启动有依赖关系,饿汉无法控制。假设:A和B两个单例,假设要求A先初始化,B再初始化,饿汉无法保证。
  • 这些问题可以通过懒汉模式来解决。

懒汉模式

什么是懒汉模式呢?同样类比于人吃饭,如果一个人很懒,他每次吃饭都要等到准备吃的时候再去洗碗;这个人还是程序,碗还是数据,只不过数据加载的时机不同,相当于程序跑起来之后再去加载数据。懒汉模式代码如下:

    class Single
	{
	public:
		static Single* GetInstance()
		{
			if (_psin == nullptr)
			{
				_psin = new Single;
			}

			return _psin;
		}
		static void DelInstance()
		{
			if (_psin)
			{
				delete _psin;
				_psin = nullptr;
			}
		}
		void AddStr(const string& s) {_vstr.push_back(s);}
		Single(Single const&) = delete;
		Single& operator=(Single const&) = delete;
	private:
		Single(int x = 0, int y = 0, const vector<string>& vstr = { "yyyyy","xxxx" })
			:_x(x)
			, _y(y)
			, _vstr(vstr)
		{}
		~Single() {cout << "~Singleton()" << endl;}

		int _x;
		int _y;
		vector<string> _vstr;
		static Single* _psin;
	};
	Single* Single::_psin = nullptr;
  • 懒汉模式中,获取实例对象是通过,new操作符来获取的,并且是第一次使用实例对象时,创建对象,而且也只能是在第一次使用实例对象时,创建对象。
  • 可以看出来,懒汉模式是一种延时加载的技术。
  • 9
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值