C++ 特殊类设计

C++11一共提供了12种特殊类(自己设计的小类没计算)

Singleton Pattern(单例模式)

Factory Pattern(工厂模式)

Abstract Base Class(抽象基类)

Proxy Class(代理类)

Flyweight Pattern(享元模式)

Observer Pattern(观察者模式)

Smart Pointers(智能指针)

Functor / Function Object(仿函数/函数对象

Template Class(模板类)

Singleton with Thread Safety(线程安全的单例模式)

Mixin Class(混合类)

CRTP (Curiously Recurring Template Pattern,奇异递归模板模式)

自己设计类

只能在堆上创建对象

class HeapOnly
{
public:
	static HeapOnly* CreateObject()
	{
		return new HeapOnly;
	}
private:
	HeapOnly() {}
	// C++98
	// 1.只声明,不实现。因为实现可能会很麻烦,而你本身不需要
	// 2.声明成私有
	HeapOnly(const HeapOnly&);
	// C++11 
	HeapOnly(const HeapOnly&) = delete;
};

1. 将类的构造函数私有,拷贝构造声明成私有。防止别人调用拷贝在栈上生成对象。

2. 提供一个静态的成员函数,在该静态成员函数中完成堆对象的创建

只能在栈上创建对象

方法一:私有化

class StackOnly 
{ 
public: 
 static StackOnly CreateObject() 
 { 
 return StackOnly(); 
 }
private:
 StackOnly() {}
};

方法二:屏蔽new

因为new在底层调用void* operator new(size_t size)函数,只需将该函数屏蔽掉即可。 注意:也要防止定位new

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

类不能被拷贝

拷贝只会放生在两个场景中:

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

A a1
两种拷贝形式
A a2(a1)
A a3 = a1
class CopyBan
{
private:
 //C++98
 CopyBan(const CopyBan&);
 CopyBan& operator=(const CopyBan&);
 //C++11
 CopyBan(const CopyBan&)=delete;
 CopyBan& operator=(const CopyBan&)=delete;

};

原因:

1. 设置成私有:类外无法访问类中private成员

2. 只声明不定义:不定义是因为该函数根本不会调用,定义了其实也没有什么意义,不写反而还简 单,而且如果定义了就不会防止成员函数内部拷贝了。

类不能被继承

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

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

只能创建一个对象(单例模式)

使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。

设计模式使代码编 写真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。

 单例模式:

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

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

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

概念和优缺点

饿汉模式:提前(main函数启动前)创建好实例对象

        优点:简单

        缺点:1.可能会导致进程启动慢,且如果有多个单例类对象实例启动顺序不确定。

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

懒汉模式(延迟加载):第一次用的时候再创建(现吃现做)

        优点:1.第一次使用实例对象时,创建对象。进程启动无负载。多个单例实例启动顺序自由控制。

                   2.懒汉模式在进程结束后自动结束释放

        缺点:复杂

静态的成员变量就是全局变量--能访问类里的私有

静态的成员函数就是全局函数--能访问类里的私有

饿汉模式
class Singleton
{
public:
	//静态成员函数访问静态成员变量和其他静态成员函数,但不能直接访问非静态成员变量或调用非静态成员函数。
	//静态成员函数or对象 在程序启动时(即main函数执行之前)就已经生成,并在程序结束时销毁。
	static Singleton* GetInstance()
	{
		return &m_instance;
	}

private:
	// 构造函数私有
	Singleton() {};

	// C++98 防拷贝
	Singleton(Singleton const&);
	Singleton& operator=(Singleton const&);

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

	static Singleton m_instance;
	//静态成员变量可以在没有创建类的实例的情况下访问,通过类名和作用域解析运算符::
	//类可以包含静态成员变量,这些变量由类的所有实例共享同一份静态成员变量
};

Singleton Singleton::m_instance; // 在程序入口之前就完成单例对象的初始化
实例

class A
{
public:
	static A* GetInstance()//函数相当于已经创建好的对象
	{
		return &_obj;
	}
	void Add(const string& key, const string& value)
	{
		_dict[key] = value;
	}

	void Print()
	{
		for (auto& kv : _dict)
		{
			cout << kv.first << ":" << kv.second << endl;
		}
		cout << endl;
	}

private:
	A() {}

	A(const A& aa) = delete;//删除默认的拷贝和赋值
	A& operator=(const A& aa) = delete;//禁用拷贝同时也要禁用赋值
private:
	int _n = 0;
	map<string, string> _dict;
	static A _obj;
};

A A::_obj;

int main()
{
	//A aa1;//报错"A::A()"已声明不可访问--无法再创建对象--构造函数在private中

	A::GetInstance()->Add("left", "左边");
	A::GetInstance()->Print();

	//类比---为了在main启动前创建一个对象制造一个static A* GetInstance()
	//让其返回return &_obj;对象地址 再通过->指针去访问&_obj对象里的成员函数
	// 和下面相比多加了一层静态函数的包装
	//A a;
	//a.Add("left", "左边");
	//a.Print();

	//A copy(*A::GetInstance());//类比A copy(a);不会再通过拷贝构造去创建一个新对象
	//copy.Print();

	//*A::GetInstance() = *A::GetInstance();//禁用拷贝同时也要禁用赋值--类比*&_obj==_obj

	return 0;
}

懒汉模式(延迟加载)
实例
class B
{
public:
	static B* GetInstance()
	{
		if (_inst == nullptr)
		{
			_inst = new B;//这里从static转变到了正常B类
		}

		return _inst;
	}

	void Add(const string& key, const string& value)
	{
		_dict[key] = value;
	}

	void Print()
	{
		for (auto& kv : _dict)
		{
			cout << kv.first << ":" << kv.second << endl;
		}
		cout << endl;
	}

	static void DelInstance()//销毁的指针是static类型,销毁函数用static静态函数
	{
		if (_inst)
		{
			delete _inst;
			_inst = nullptr;//全局指针,及时置空
		}
	}
private:
	B(){}

	~B()
	{
		// 持久化:要求把数据写到文件
		//完成该对象结束前需要进行的一些操作
		//类似关机前电脑会转几圈(保存数据)后在关机,同理
		cout << "数据写到文件" << endl;
	}

	B(const B& aa) = delete;
	B& operator=(const B& aa) = delete;

	//gc类放在private中防止在类外创建新的gc对象导致单例类对象提前释放
	class gc//内部类可以访问外部类-直接访问外部类中的static
	{
	public:
		//_gc是普通的一个什么都没有的gc类型对象,所以释放时不需要任何操作
		~gc()
		{
			//_gc是一个对象程序结束都能自己去调用析构函数
			//但是类似_inst这样的指针则不能自动去调用析构函数
			//通过_gc的自动调用析构,在其析构里在加入销毁指针的函数
			DelInstance();
		}
	};
private:
	map<string, string> _dict;
	int _n = 0;

	static B* _inst;
	static gc _gc;//主要为了能在销毁/释放B类前进行一些操作而增加,同时能自动析构
};

B* B::_inst = nullptr;
//gc 类型对象_gc全局一直存在,知道程序结束
B::gc B::_gc;
int main()
{
	//用的时候创建(现吃现做)
	B::GetInstance()->Add("left", "左边");
	B::GetInstance()->Print();

	// 期望main函数结束后自动调用DelInstance();

	return 0;
}

饿汉、懒汉程序对比

1.类对象创建顺序对比

饿汉模式:

static A* GetInstance()
	return &_obj;
}

static A _obj;
A A::_obj;

饿汉模式通过在程序运行前创建全局变量(静态的成员变量)获得单例的类对象

懒汉模式:

static B* GetInstance()
	{
		if (_inst == nullptr)
		{
			_inst = new B;//这里从static转变到了正常B类
		}

		return _inst;
	}

static B* _inst;
B* B::_inst = nullptr;

懒汉模式的静态的成员变量先创建指针,在使用时B::GetInstance()再去static B* GetInstance()函数中获取单例类的对象

2.防止创建新对象

单例类的目的就是只能有一个对象,或者说一个实例化对象

不管在饿汉模式或懒汉模式下都需要确保不能创建新类

public:
	static A* GetInstance()//外部函数包装,用于访问private保护内容
	{
		return &_obj;
	}
private:
	A() {}
	A(const A& aa) = delete;//删除默认的拷贝和赋值
	A& operator=(const A& aa) = delete;//禁用拷贝同时也要禁用赋值

将构造,赋值,拷贝都放在类中的private中保护,只能通过类中的外部函数去访问保护内容从而避免主函数或测试函数中创建出新的类对象(避免主函数或测试函数无法访问private保护内容就无法创建新的类对象)

  • 16
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值