C++进阶 — 特殊类设计

目录

1. 设计一个不能被拷贝的类

2. 设计一个只能在堆上创建对象的类

 3. 设计一个只能在栈上创建对象的类

4. 设计一个不能被继承的类

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

  单例模式

   饿汉模式

   懒汉模式


1. 设计一个不能被拷贝的类

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

在C++98中只声明拷贝构造和赋值重载,不实现,且把这两个成员函数设为私有即可。设置私有后类外面就调用不到了,有了声明,编译器就不会默认生成了。

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

在C++11中扩展了delete的用法,delete除了释放new申请的资源外,如果在默认成员函数后跟上=delete,表示让编译器删除掉该默认成员函数。

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

2. 设计一个只能在堆上创建对象的类

先将类的构造函数私有,拷贝构造声明成私有。防止别人调用拷贝构造在栈上生成对象。再提供一个静态的成员函数,在该静态成员函数中完成堆对象的创建。

class HeapOnly
{
public:
	static HeapOnly* createOBJ()
	{
		return new HeapOnly;
	}
private:
	HeapOnly()
    {}
	HeapOnly(const HeapOnly&) = delete;
	
};

 还可以将析构函数设为私有。释放空间时通过类内的接口去释放即可。


 3. 设计一个只能在栈上创建对象的类

同上将构造函数私有化,然后设计静态方法创建对象返回即可,这里需要注意,不能完全禁止在其他的地方创建对象。
 


4. 设计一个不能被继承的类

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

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

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

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

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

设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。

使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 设计模
式使代码编写真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。

比如迭代器模式,配接器(适配器)模式,单例模式,工厂模式,观察者模式等。这些都是设计模式。

  单例模式

一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。

比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,这种方式简化了在复杂环境下的配置管理。

单例模式有两种实现模式:

   饿汉模式

程序启动时(在main函数之前)就创建一个唯一的实例对象。

class Singleton
{
public:
	static Singleton& create()
	{
		return _sins;
	}
    
	void Insert(string s, int money)
	{
		//_info.insert(make_pair(s, money));
		_info[s] = money;
	}
	void Print()
	{
		for (const auto& kv : _info)
		{
			cout << kv.first << ":" << kv.second << endl;
		}
		cout << endl;
	}
private:

	Singleton()
	{}
    Singleton(const Singleton&) = delete;
	Singleton& operator=(const Singleton&) = delete;
private:
	map<string, int> _info;
	static Singleton _sins;
};
Singleton Singleton::_sins;//初始化

 可以看到确实是只有一个对象的,无法拷贝和赋值。

优点:1.简单
           2.单例对象在多线程高并发环境下频繁使用,性能要求较高,那么显然使用饿汉模式来避免资源竞争,提高响应速度更好。

缺点:1.单例对象初始化时数据太多,导致启动慢;

           2.多个单例类初始化有依赖关系,饿汉模式无法控制。因为多个单例类对象实例启动顺序是不确定的。

   懒汉模式

对象在main函数之后才会创建,不会影响启动顺序,也可以主动控制创建顺序。

如果单例对象构造十分耗时或者占用很多资源,比如加载插件啊, 初始化网络连接啊,读取文件啊等等,而有可能该对象程序运行时不会用到,那么也要在程序一开始就进行初始化,就会导致程序启动时非常的缓慢。 所以这种情况使用懒汉模式(延迟加载)更好。
//template<class lock>
//class lockGuard //智能指针->自动加锁,解锁	RAII方式
//{
//public:
//	lockGuard(lock& lk)
//		:_lk(lk)
//	{
//		_lk.lock();
//	}
//	~lockGuard()
//	{
//		_lk.unlock();
//	}
//private:
//	lock& _lk;
//};
//懒汉模式
class Singleton
{
public:
	//static Singleton& create()
	//{
	//	//第一次获取单列对象的时候创建对象
	//	//双检查加锁
	//	if (_psins == nullptr)//对象new出来以后,避免每次都加锁检查,提高性能
	//	{
	//		_mtx.lock();
	//		if (_psins == nullptr)//保证线程安全且只new一次
	//		{
	//			_psins = new Singleton;		//如果new抛异常,就没有解锁
	//		}
	//		_mtx.unlock();
	//	}
	//	return *_psins;
	//}
	
	static Singleton& create()
	{
		//第一次获取单列对象的时候创建对象
		//双检查加锁
		if (_psins == nullptr)//对象new出来以后,避免每次都加锁检查,提高性能
		{
			//lockGuard<mutex> mtx(_mtx);    //自己实现的
			std::lock_guard<mutex> mtx(_mtx);//库里提供的

			if (_psins == nullptr)//保证线程安全且只new一次
			{
				_psins = new Singleton;
			}
		}
		return *_psins;
	}
	//插入
	void Insert(string s, int money)
	{
		//_info.insert(make_pair(s, money));
		_info[s] = money;
	}

	void Print()
	{
		for (const auto& kv : _info)
		{
			cout << kv.first << ":" << kv.second << endl;
		}
		cout << endl;
	}
	//一般单例对象不需要考虑释放
	//单例对象不用时,必须手动处理,一些资源需要保存
	static void DelInstance()
	{
		//保存数据到文件
		//...
		std::lock_guard<mutex> lock(_mtx);
		if (_psins)
		{
			delete _psins;
			_psins = nullptr;
		}
	}
	//自动回收
	class GC
	{
	public:
		~GC()
		{
			if (_psins)//如果你没有显示的释放,我就帮你回收
			{
				cout << "~GC()" << endl;
				DelInstance();	//内部类是外部类的友元,可以直接调用
			}
		}
	};
	
private:
	Singleton()
	{}
	Singleton(const Singleton&) = delete;
	Singleton& operator=(const Singleton&) = delete;
private:
	map<string, int> _info;
	static Singleton* _psins;
	static mutex _mtx;
	static GC _gc;
};
Singleton* Singleton::_psins = nullptr;//初始化
mutex Singleton::_mtx;
Singleton::GC Singleton::_gc;

优点:第一次使用实例对象时,创建对象。进程启动无负载。多个单例实例启动顺序自由控
制。
缺点:复杂,需要注意很多问题。

完整代码:特殊类/特殊类/test.cpp · 晚风不及你的笑/作业库 - 码云 - 开源中国 (gitee.com)

拓展:

//懒汉模式
class Singleton
{
public:

	static Singleton& create()
	{
		static Singleton sins;    //只会创建一次
		return sins;
	}
	//插入
	void Insert(string s, int money)
	{
		//_info.insert(make_pair(s, money));
		_info[s] = money;
	}

	void Print()
	{
		for (const auto& kv : _info)
		{
			cout << kv.first << ":" << kv.second << endl;
		}
		cout << endl;
	}
private:
	Singleton()
	{
		cout << "Singleton()" << endl;
	}
	Singleton(const Singleton&) = delete;
	Singleton& operator=(const Singleton&) = delete;
private:
	map<string, int> _info;
};

 这个也是懒汉模式,因为静态的局部变量是在main函数之后才创建初始化的。

在C++11之前是不能保证sins的初始化是线程安全的,在C++11之后是可以保证的。因为C++11之前并没有对这一块进行规定,每个编译器实现是不同的,C++11之后规定,静态的局部变量必须是线程安全的。网上有很多这方面的说明,可以自己去查一查。

本资源是压缩包形式的, 里面包含 本书,里面是pdf格式的, 带书签目录,本书是完整版的。 资源都是我自己用过的,不骗大家。 本书作者: 肖俊宇 吴为胜; 出版社: 电子工业出版社 内容简介: 《由浅入深学C++:基础、进阶与必做300题(含DVD光盘1张)》是C++语言的入门教程,较为系统地介绍了C++语言的基础内容。本书共分为3篇22章,详细介绍了C++语言的基础知识、面向对象、标准模块、底层开发和综合案例。本书循序渐进地讲述了C++的基础知识、C++程序的组成及其开发过程、C++程序中的数据、表达式和语句、控制程序流程、数组与字符串、指针与引用、使用函数、函数模板、错误和异常处理、宏和预编译、面向对象的开发、封装、继承、多态、类模板、文件流、标准模板库STL和编程实践等内容。 《由浅入深学C++:基础、进阶与必做300题(含DVD光盘1张)》涉及面广,从基本知识到高级内容和核心概念,再到综合案例,几乎涉及C++开发的所有重要知识。本书适合所有想全面学习C++开发技术的人员阅读,尤其适合没有编程基础的C++语言初学者作为入门教程,也可作为大、中院校师生和培训班的教材,对于C++语言开发爱好者,本书也有较大的参考价值。 章节目录: 第1篇 C++基础篇 第1章 C++概述 1 1.1 引言 1 1.1.1 C++的历史沿革 1 1.1.2 入门C++ 2 1.1.3 编程思想的转变 3 1.2 C++概述 4 1.2.1 C++的特征 5 1.2.2 C与C++的比较 5 1.2.3 C++的应用领域 6 1.3 C++源程序的组成 6 1.3.1 基本组成元素 7 1.3.2 标识符 8 1.3.3 保留字 8 1.3.4 符号 8 1.4 C++集成开发环境——DEV-C++ 9 1.4.1 选择C++编译器 9 1.4.2 安装DEV-C++ 10 1.4.3 DEV-C++ IDE简介 11 1.5 第一个C++程序——Hello World 11 1.5.1 创建源程序 11 1.5.2 编译运行 13 1.6 小结 14 1.7 习题 14 第2章 变量与数据类型 18 2.1 常量和变量 18 2.1.1 常量 18 2.1.2 变量 21 2.1.3 变量的定义及赋值 22 2.1.4 变量的应用示例 24 2.2 基本数据类型 25 2.2.1 基本数据类型概述 25 2.2.2 整型数据类型 26 2.2.3 浮点型数据类型 27 2.2.4 字符型数据类型 29 2.2.5 布尔型数据类型 30 2.3 变量的作用域 31 2.4 类型转换 32 2.4.1 隐式转换 32 2.4.2 显式转换 33 2.5 小结 34 2.6 习题 34 第3章 表达式与语句 39 3.1 运算符 39 3.1.1 运算符概述 39 3.1.2 算术运算符 40 3.1.3 自增和自减运算符 42 3.1.4 赋值运算符 43 3.1.5 关系运算符 44 3.1.6 逻辑运算符 45 3.1.7 条件运算符 46 3.1.8 逗号运算符 47 3.1.9 位运算符 48 3.1.10 sizeof运算符 49 3.2 运算符的优先级和结合性 50 3.3 表达式 51 3.4 语句 53 3.4.1 空格的作用 53 3.4.2 语句块 54 3.4.3 赋值语句 55 3.4.4 空语句 56 3.5 小结 57 3.6 习题 57 第4章 流程控制结构之顺序结构 63 4.1 程序流程图 63 4.2 表达式语句 64 4.3 格式化输入/输出 65 4.3.1 标准输入流cin 65 4.3.2 标准输出流cout 66 4.3.3 输出流cerr和clog 68 4.4 格式控制函数 69 4.5 格式控制符 71 4.5.1 控制不同进制的输出 72 4.5.2 控制输出宽度 72 4.5.3 控制输出精度 73 4.6 顺序结构综合应用 74 4.7 小结 75 4.8 习题 75
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

晚风不及你的笑427

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值