【C++】特殊类设计

目录

设计一个类,不能被拷贝

设计一个类,只在堆上创建对象

设计一个类,只能在栈上创建对象

设计一个类,不能被继承

设计一个类,只能创建一个对象(单例模式)


设计一个类,不能被拷贝

通过前面的学习,我们知道拷贝会发生在两种情况下,拷贝构造和赋值运算符重载,因此,想要一个类禁止拷贝,只需要让这个类不能调用拷贝构造函数和赋值重载即可。

C++98

将拷贝构造函数与赋值运算符重载只声明不定义,并且将其访问权限设置为私有即可。

class Config
{
	//...
private:
	Config(const Config& con);
	Config& operator=(const Config& con);
};

C++11

C++11中,在默认成员函数后面加上“=delete”,就可以让改编译器删除该成员函数。

class Config
{
	//...
	Config(const Config& con) = delete;
	Config& operator=(const Config& con) = delete;
};

设计一个类,只在堆上创建对象

class HeapOnly
{
public:
	//设计成静态成员函数,不需要对象就能调用
	static HeapOnly* CreateObj()
	{
		return new HeapOnly();
	}
private:
	//构造函数不能delete,因为CreateObj还需要用到,只能设为私有
	HeapOnly() {};
	//C++98
	HeapOnly(const HeapOnly&);

	//C++11
	HeapOnly(const HeapOnly&) = delete;
	HeapOnly& operator=(const HeapOnly&) = delete;  
};

int main()
{
	HeapOnly* h = HeapOnly::CreateObj();
	return 0;
}

还有人提出一种奇葩的写法:将析构函数私有化,

class HeapOnly
{
public:
	void Release()
	{
		this->Release();
	}
private:
	//析构函数私有化
	~HeapOnly()
	{
	}
};
int main()
{
	HeapOnly* obj = new HeapOnly;
	obj->Release();

	return 0;
}

这样,构造、拷贝构造、赋值因为不能调用析构函数而不能创建对象,但是,在堆上创建的对象可以调用Release,Release再调用析构函数。

设计一个类,只能在栈上创建对象

class StackOnly
{
public:
	//不能把拷贝构造delete,这里传值返回需要用到
	static StackOnly CreateObj()
	{
		return StackOnly();
	}
	// 禁掉operator new可以把下面用new 调用拷贝构造申请对象给禁掉
	// StackOnly obj = StackOnly::CreateObj();
	// StackOnly* ptr3 = new StackOnly(obj);//这里的new不会调全局的operator new,而是调自己实现的
	// operator new是针对这个类的局部的
	void* operator new(size_t size) = delete;
	void operator delete(void* p) = delete;
private:
	StackOnly()
		:_a(0)
	{}
private:
	int _a;
};

设计一个类,不能被继承

C++98

// C++98中构造函数私有化,派生类中调不到基类的构造函数。则无法继承
class NonInherit
{
    public:
    static NonInherit GetInstance()
    {
        return NonInherit();
    }
private:
    NonInherit()
    {}
};

C++11

final关键字,final修饰类,表示该类不能被继承。

class A final
{
    // ....
};

设计一个类,只能创建一个对象(单例模式)

单例模式,就是希望全局只有一个对象,不能被创建出两份,比如,配置信息的类,只要有一份就行。

单例模式有两种设计模式:

饿汉模式:不管你用不用,程序启动时就会创建一个唯一的实例对象。

所谓饿汉模式,就是早早地创建对象,在进入main函数之前就创建。

class ConfigInfo
{
public:
	static ConfigInfo* GetInstance()
	{
		return &_sInfo;
	}

	string GetIp()
	{
		return _ip;
	}

	void SetIp(const string& ip)
	{
		_ip = ip;
	}
private:
	//把构造私有,这样就不能在外部创建
	ConfigInfo() {};
	//拷贝构造和赋值delete 
	ConfigInfo(const ConfigInfo&) = delete;
	ConfigInfo& operator=(const ConfigInfo&) = delete;

	
private:
	string _ip = "127.0.0.1";
	int _port = 80;
	//...
	//声明,不属于类对象,是在静态区,只不过受类域限制
	static ConfigInfo _sInfo;
};

//饿汉模式,在进入main之前就创建对象
//定义
ConfigInfo ConfigInfo::_sInfo;

int main()
{
	cout << ConfigInfo::GetInstance() << endl;
	ConfigInfo::GetInstance()->SetIp("196.13.1");
	cout << ConfigInfo::GetInstance()->GetIp() << endl;

	return 0;
}

但是,饿汉模式存在两个问题:

1.如果很多单例类都是饿汉模式,或者有些单例类初始化资源很多,导致程序启动慢,迟迟进不了main函数。

2.如果两个单例类有初始化依赖关系,饿汉也无法解决。比如,A类和B类是单例类,A单例要连接数据库,B单例要用A单例初始化数据库。

为了解决以上问题,又有了懒汉模式

懒汉模式:第一次调用GetInstance时创建对象。

懒汉模式不在main函数前创建对象,这就不会导致程序启动慢,另外,尽管A单例类和B单例类存在依赖关系,我可以在main函数中通过改变A和B的GetInstance创建对象的顺序,来适应它们的依赖关系,懒汉完美解决了饿汉的问题

class ConfigInfo
{
public:
	static ConfigInfo* GetInstance()
	{
		//定义局部静态单例对象,只会创建一次
		static ConfigInfo _sInfo;
		return &_sInfo;
	}

	string GetIp()
	{
		return _ip;
	}

	void SetIp(const string& ip)
	{
		_ip = ip;
	}
private:
	//把构造私有,这样就不能在外部创建
	ConfigInfo() 
	{
		cout << "ConfigInfo()" << endl;
	};
	//拷贝构造和赋值delete 
	ConfigInfo(const ConfigInfo&) = delete;
	ConfigInfo& operator=(const ConfigInfo&) = delete;


private:
	string _ip = "127.0.0.1";
	int _port = 80;
};

这种方式有一点小小的缺陷,C++11之前,多线程调用GetInstance,局部静态单例对象构造初始化,无法保证进程安全,编译器支持C++11才可以用,因为C++11对这个问题单独处理了。但是现在编译器基本都支持C++11,所以这种写法很完美!

那在C++11之前,我们是怎么实现懒汉的呢?(了解)

和饿汉模式类似,但是是定义一个静态指针_pInfo=nullptr,指针在main函数前初始化问题不大,初始化一个指针而已,然后第一次调用GetInstance的时候,判断_pInfo是否为空,为空则创建对象,等下一次调用GetInstance的时候,_pInfo不为空,就不会创建对象。为了保证多线程调用时的安全,需要加锁。为了避免每次调用GetInstance都要加锁,使用双检查机制加锁。

//C++11前的懒汉模式
class ConfigInfo
{
public:
	static ConfigInfo* GetInstance()
	{
		//只有第一次调用时,_pInfo才不等于空,才会进入
		//多线程调用需要考虑线程安全的问题
		//这样,当t1 t2两个线程来的时候,只有一个线程能进
		//但是这样会每次调用GetInstace的时候,都会加这个锁,但其实没必要,只用第一次加锁就行
		/*unique_lock<mutex> lock(_mtx);
		if (_pInfo == nullptr)
		{
			_pInfo = new ConfigInfo;
		}*/
		//所以,使用双检查机制
		if (_pInfo == nullptr)    //性能
		{
			unique_lock<mutex> lock(_mtx);
			if (_pInfo == nullptr)//线程安全
			{
				_pInfo = new ConfigInfo;
			}
		}

		return _pInfo;
	}

	string GetIp()
	{
		return _ip;
	}

	void SetIp(const string& ip)
	{
		_ip = ip;
	}
private:
	//把构造私有,这样就不能在外部创建
	ConfigInfo()
	{
		cout << "ConfigInfo()" << endl;
	};
	//拷贝构造和赋值delete 
	ConfigInfo(const ConfigInfo&) = delete;
	ConfigInfo& operator=(const ConfigInfo&) = delete;
private:
	string _ip = "127.0.0.1";
	int _port = 80;

	static ConfigInfo* _pInfo;
	static mutex _mtx;
};

ConfigInfo* ConfigInfo::_pInfo = nullptr;
mutex ConfigInfo::_mtx;

int main()
{
	cout << ConfigInfo::GetInstance() << endl;
	cout << ConfigInfo::GetInstance() << endl;
	ConfigInfo::GetInstance()->SetIp("196.13.1");
	cout << ConfigInfo::GetInstance()->GetIp() << endl;
	return 0;
}

  • 15
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值