特殊类的设计(含单例模式)

这篇具有很好参考价值的文章主要介绍了特殊类的设计(含单例模式)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


一、设计一个不能被拷贝的类

拷贝只会放生在两个场景中:拷贝构造函数以及赋值运算符重载,因此想要让一个类禁止拷贝,只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。

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;
    //...
};

二、设计一个只能在堆上创建的类

方式一:

  1. 将类的构造函数私有,拷贝构造声明成私有。防止别人调用拷贝在栈上生成对象。
  2. 提供一个静态的成员函数,在该静态成员函数中完成堆对象的创建。
class HeapOnly {
public:
	static HeapOnly* CreatObject() { return new HeapOnly(); }
private:
	HeapOnly() {}
	HeapOnly(const HeapOnly&) = delete;
};

这种方式需要提供创建对象的接口,那么有没有其它方式呢?

特殊类的设计(含单例模式),单例模式,linux,数据结构,c++,c语言

方式二:

  1. 将析构函数设置为私有。
  2. 另外生成一个public权限函数来,释放对象。
class HeapOnly {
public:
	HeapOnly() {}
private:
	~HeapOnly() {}
};

原因:C++是静态绑定语言,编译器管理栈上对象的生命周期,编译器在为类对象分配栈空间时,会先检查类的析构函数的访问性。若析构函数不可访问,则不能在栈上创建对象。

特殊类的设计(含单例模式),单例模式,linux,数据结构,c++,c语言

三、设计一个只能在栈上创建的类

方法:将 new 和 delete 重载为私有。

在堆上生成对象,使用new关键词操作,其过程分为两阶段:第一阶段,使用new在堆上寻找可用内存,分配给对象;第二阶段,调用构造函数生成对象。

将new操作设置为私有,那么第一阶段就无法完成,就不能够再堆上生成对象。

class StackOnly {
private:
	int _a;
public:
	void* operator new(size_t size) = delete;
	void operator delete(void* p) = delete;

	StackOnly() :_a(0) {}

};

四、设计一个不能被继承的类

C++98:构造函数私有化,派生类中调不到基类的构造函数。则无法继承

class NonInherit
{
public:
 	static NonInherit GetInstance(){ return NonInherit();}
private:
 	NonInherit() {}
};

C++11:final关键字,final修饰类,表示该类不能被继承

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

五、单例模式

一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个
访问它的全局访问点,该实例被所有程序模块共享。比如在某个服务器程序中,该服务器的配置
信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再
通过这个单例对象获取这些配置信息,这种方式简化了在复杂环境下的配置管理。

1.懒汉模式

如果单例对象构造十分耗时或者占用很多资源,比如加载插件,初始化网络连接,读取文件等,而有可能该对象程序运行时不会用到,那么也要在程序一开始就进行初始化,就会导致程序启动时非常的缓慢。 所以这种情况使用懒汉模式(延迟加载)更好。

懒汉模式version1 -> 问题1:线程安全 问题2:内存泄露

class Singleton {
private:
	static Singleton* _instance;
	Singleton() { cout << "构造函数" << endl; }
	Singleton(const Singleton& s) = delete;
	Singleton& operator=(const Singleton&) = delete;
public:
	static Singleton* getInstance()
	{
		if (_instance == nullptr)
			_instance = new Singleton();
		return _instance;
	}
	~Singleton() { cout << "析构函数" << endl; }

};
Singleton* Singleton::_instance = nullptr;

运行上面的代码我们发现资源没有释放:

特殊类的设计(含单例模式),单例模式,linux,数据结构,c++,c语言

针对资源释放的问题,我们可以通过实现一个内嵌的垃圾回收类,定义一个静态成员变量,程序结束时,系统会自动调用它的析构函数从而释放单例对象:

class Singleton {
public:
	static Singleton* getInstance()
               	{
		if (_instance == nullptr)
			_instance = new Singleton();
		return _instance;
	}
	~Singleton() { cout << "析构函数" << endl; }

public:
	// 实现一个内嵌垃圾回收类    
	class CGarbo {
	public:
		~CGarbo() { 
			if (Singleton::_instance != nullptr) { delete Singleton::_instance; }
		}
	};
	// 定义一个静态成员变量,程序结束时,系统会自动调用它的析构函数从而释放单例对象
	static CGarbo Garbo;

private:
	static Singleton* _instance;
	Singleton() { cout << "构造函数" << endl; }
	Singleton(const Singleton& s) = delete;
	Singleton& operator=(const Singleton&) = delete;
};
Singleton* Singleton::_instance = nullptr;
Singleton::CGarbo Garbo;

懒汉模式version2 -> 不足:要求使用智能指针,锁有开销

我们不仅可以用内部类的思想来解决资源泄露的问题,还可以使用智能指针:


class Singleton {
public:
	typedef shared_ptr<Singleton> Ptr;
	static Ptr getInstance()
	{
		if (_instance == nullptr)
		{
			std::lock_guard<std::mutex> _lck(_mtx);
			if (_instance == nullptr)
				_instance = shared_ptr<Singleton>(new Singleton());
		}
		return _instance;
	}
	~Singleton() { cout << "析构函数" << endl; }

private:
	static Ptr _instance;
	static std::mutex _mtx;
private:
	Singleton() { cout << "构造函数" << endl; }
	Singleton(const Singleton& s) = delete;
	Singleton& operator=(const Singleton&) = delete;
};
Singleton::Ptr Singleton::_instance = nullptr;
std::mutex Singleton::_mtx;

shared_ptrmutex都是C++11的标准,以上这种方法的优点是:

  • 基于 shared_ptr, 用了C++比较倡导的 RAII思想,用对象管理资源,当 shared_ptr 析构的时候,new 出来的对象也会被 delete掉。以此避免内存泄漏。
  • 加了锁,使用互斥量来达到线程安全。这里使用了两个 if 判断语句的技术称为双检锁;好处是,只有判断指针为空的时候才加锁,避免每次调用 get_instance的方法都加锁,提高效率。

不足之处在于:

  • 使用智能指针会要求用户也得使用智能指针,非必要不应该提出这种约束; 使用锁也有开销; 同时代码量也增多了,实现上我们希望越简单越好。
  • 还有更加严重的问题,在某些平台(与编译器和指令集架构有关),双检锁会失效!

懒汉模式version3 -> Meyers’s 的单例

C++11 规定了 local static 在多线程条件下的初始化行为,要求编译器保证了内部静态变量的线程安全性。在 C++11 标准下,《 Effective C++》提出了一种更优雅的单例模式实现,使用函数内的 local static 对象。这样,只有当第一次访问getInstance()方法时才创建实例。这种方法也被称为Meyers' Singleton

class Singleton {
private:
	Singleton() { cout << "构造函数" << endl; }
	~Singleton() { cout << "析构函数" << endl; }
	Singleton(const Singleton& s) = delete;
	Singleton& operator=(const Singleton&) = delete;
public:
	static Singleton& getInstance()
	{
		static Singleton _instance;
		return _instance;
	}
};

2.饿汉模式

单例实例在程序运行时被立即执行初始化,如果这个单例对象在多线程高并发环境下频繁使用,性能要求较高,那么显然使用饿汉模式来避免资源竞争,提高响应速度更好。:文章来源地址https://www.toymoban.com/news/detail-817681.html

class Singleton
{
private:
	static Singleton _instance;
private:
	Singleton() { cout << "构造函数" << endl; }
	~Singleton() { cout << "析构函数" << endl; }
	Singleton(const Singleton&) = delete;
	Singleton& operator=(const Singleton&) = delete;
public:
	static Singleton& getInstance() { return _instance; }
};
Singleton Singleton::_instance;

到了这里,关于特殊类的设计(含单例模式)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++】特殊类设计(单例模式)

    设计模式是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。 使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 根本原因是为了代码复用,增加可维护性。 设计模式的例子:迭代器模式 拷贝一共就只有两个场景,一

    2023年04月22日
    浏览(44)
  • C++特殊类设计(单例模式)

    C++98 将拷贝构造函数与赋值运算符重载只声明不定义,并且将其访问权限设置为私有即可。 原因: 设置成私有:如果只声明没有设置成private,用户自己如果在类外定义了,就可以不能禁止拷贝了 只声明不定义:不定义是因为该函数根本不会调用,定义了其实也没有什么意义

    2024年01月19日
    浏览(38)
  • 【C++】特殊类设计+单例模式+类型转换

    需要云服务器等云产品来学习Linux的同学可以移步/--腾讯云--/--阿里云--/--华为云--/官网,轻量型云服务器低至112元/年,新用户首次下单享超低折扣。   目录 一、设计一个类,不能被拷贝 1、C++98 2、C++11 二、设计一个类,只能在堆上创建对象 1、将构造设为私有 2、将析构设为

    2024年02月06日
    浏览(44)
  • 从C语言到C++_37(特殊类设计和C++类型转换)单例模式

    目录 1. 特殊类设计 1.1 不能被拷贝的类 1.2 只能在堆上创建的类 1.3 只能在栈上创建的类 1.4 不能被继承的类 1.5 只能创建一个对象的类(单例模式)(重点) 1.5.1 饿汉模式 1.5.2 懒汉模式 2. 类型转换 2.1 static_cast 2.2 reinterpret_cast 2.3 const_cast 2.4 dynamic_cast 3. RTTI(了解)和类型转换常见面

    2024年02月10日
    浏览(34)
  • C++:特殊类和单例模式

    设计一个不能被拷贝的类,通常来说方法就是把拷贝构造和赋值重载都设置为私有,这样就不能进行拷贝了 对于C++11来说,新增了delete的用法,可以直接删除这个函数,也能做到相同的效果 这是一个比较奇怪的要求,但是也有对应实现的方法: 把类的构造函数设置为私有 提

    2024年01月18日
    浏览(47)
  • 【C++】特殊类的设计

    💕 C++98方式: 在C++11之前,想要一个一个类不被拷贝,只有将 拷贝构造函数 定义为私有,这样在类外就不能调用拷贝构造函数来构造对象了。但是在类内还是可以调用拷贝构造函数来构造对象。 所以正确的做法是 将拷贝构造函数定义为私有,同时拷贝构造函数只声明,不

    2024年02月07日
    浏览(32)
  • C++进阶 特殊类的设计

    本篇博客介绍:介绍几种特殊的类 我们的拷贝只会发生在两个场景当中 拷贝构造函数 赋值运算符重载 所以说我们只需要让类失去 或者说不能使用这两个函数即可 这里有两个解决方案 在C++98中 我们将拷贝构造函数只声明不定义 并且将其访问权限设置为私有即可 原因如下

    2024年02月12日
    浏览(26)
  • C++之特殊类的设计

    目录 一、单例模式 1、设计模式 2、单例模式 1、饿汉模式 2、懒汉模式 3、单例对象的释放问题 二、设计一个不能被拷贝的类 三、设计一个只能在堆上创建对象的类 四、设计一个只能在栈上创建对象的类 五、设计一个不能被继承的类 概念: 设计模式(Design Pattern)是一套被

    2024年02月08日
    浏览(29)
  • 【Linux】简单线程池的设计与实现 -- 单例模式

    线程池: 一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而 线程池维护着多个线程,等待着监督管理者分配可并发执行的任务 。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。

    2024年02月12日
    浏览(31)
  • 【C++】特殊类的设计 | 类型转换

    设计模式是 被反复使用 多数人知晓 经过分类的、代码设计经验的总结 单例模式: 一个类只能创建一个对象 即单例模式,该模式可以保证系统中该类只有一个实例 单例模式分为饿汉模式和懒汉模式 饿汉模式 一开始就创建对象(main函数之前) 假设想要vector数组全局只有一份

    2024年02月15日
    浏览(27)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包