<C++>特殊类的设计

请设计一个类,不能被拷贝

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

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. 提供一个静态的成员函数(这样可以用定义域调用函数,不用this指针),在该静态成员函数中完成堆对象的创建
class HeapOnly
{
public:
	static HeapOnly* CreateObj()
	{
		return new HeapOnly;
	}

	HeapOnly(const HeapOnly&) = delete;// 防拷贝

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

int main()
{
	//HeapOnly h1;
	//static HeapOnly h2;
	//HeapOnly* ph3 = new HeapOnly;
	HeapOnly* ph4 = HeapOnly::CreateObj();
	HeapOnly* ph5 = HeapOnly::CreateObj();

	//HeapOnly copy(*ph4);

	delete ph4;
	delete ph5;

	return 0;
}
// 方法二:析构函数私有
class HeapOnly
{
public:
	static void DelObj(HeapOnly* ptr)
	{
		delete ptr;
	}

	/*void DelObj()
	{
	delete this;
	}*/

private:
	// 析构函数私有
	~HeapOnly()
	{}
};

int main()
{
	HeapOnly h1;
	static HeapOnly h2;
	HeapOnly* ph3 = new HeapOnly;

	delete ph3;
	HeapOnly::DelObj(ph3);
	ph3->DelObj();

	return 0;
}

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

方法一:同上将构造函数私有化,然后设计静态方法创建对象返回即可

方法二:禁掉operator new

// 方法一
class StackOnly
{
public:
	static StackOnly CreateObj()
	{
		return StackOnly();
		//StackOnly st;
		//return st;
	}

	StackOnly(const StackOnly&) = delete;// 有时编译器优化可以在构造时不调动拷贝构造,这时才能禁掉拷贝构造

	void Print()
	{
		cout << "Stack Only" << endl;
	}
private:
	// 构造函数私有
	StackOnly()
	{}
};

int main()
{
	StackOnly h1 = StackOnly::CreateObj();
	//StackOnly::CreateObj().Print();

	//static StackOnly h2;
	//StackOnly* ph3 = new StackOnly;

	return 0;
}
 // 方法二
class StackOnly
{
public:
    static StackOnly CreateObj()
    {
    	return StackOnly();
    }
    // 禁掉operator new可以把下面用new 调用拷贝构造申请对象给禁掉
    // StackOnly obj = StackOnly::CreateObj();
    // StackOnly* ptr3 = new StackOnly(obj);
    void* operator new(size_t size) = delete;
    void operator delete(void* p) = delete;
private:
    StackOnly()
    	:_a(0)
    {}
private:
    int _a;
};

int main()
{
	StackOnly obj = StackOnly::CreateObj();
	//StackOnly* ptr3 = new StackOnly(obj);
	//static StackOnly copy(obj);

	return 0;
}

请设计一个类,不能被继承

C++98方式

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

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

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

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

设计模式
设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。
使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 设计模式使代码编写真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。
单例模式
一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,这种方式简化了在复杂环境下的配置管理。

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

  • 饿汉模式 就是说不管你将来用不用,程序启动时就创建一个唯一的实例对象。
// 饿汉 -- 一开始(main函数之前)就创建
class Singleton
{
public:
	static Singleton* GetInstance()
	{
		return _spInst;// 饿汉不好写带参的初始化
	}

	void Print();
private:
	Singleton()
	{}

	Singleton(const Singleton&) = delete;


	// 放类里面是因为构造函数是私有的,类成员才能调用私有的
	//static Singleton _sInst; // 声明
	static Singleton* _spInst; // 声明

	int _a = 0;
};

//Singleton Singleton::_sInst; // 定义
Singleton* Singleton::_spInst = new Singleton; // 定义


void Singleton::Print()
{
	cout << _a << endl;
}

int main()
{
	// GetInstance() 可以获得这个Singleton类的单例对象
	Singleton::GetInstance()->Print();
	//Singleton st1;
	//Singleton* st2 = new Singleton;
	//Singleton copy(*Singleton::GetInstance());

	return 0;
}

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

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

单例模式一般不需要析构,就只有一个对象,不用怕内存泄漏。进程正常结束时,资源也会还给操作系统。

但在有的情况下也需要析构函数。举个例子:假设析构时需要信息写到文件持久化(我们需要用到垃圾回收类)

// 懒汉 -- 一开始不创建对象,第一调用GetInstance再创建对象

class InfoMgr
{
public:
	static InfoMgr* GetInstance()
	{
		// 还需要加锁,这个后面讲  -- 双检查加锁
		if (_spInst == nullptr)
		{
			_spInst = new InfoMgr;
		}

		return _spInst;
	}

	void SetAddress(const string& s)
	{
		_address = s;
	}

	string& GetAddress()
	{
		return _address;
	}

	// 实现一个内嵌垃圾回收类    
	class CGarbo 
	{
	public:
		~CGarbo() {
			if (_spInst)
				delete _spInst;
		}
	};

	// 定义一个静态成员变量,程序结束时,系统会自动调用它的析构函数从而释放单例对象
	static CGarbo Garbo;

private:
	InfoMgr()
	{}

	~InfoMgr()
	{
		// 假设析构时需要信息写到文件持久化
	}

	InfoMgr(const InfoMgr&) = delete;

	string _address;
	int _secretKey;

	static InfoMgr* _spInst; // 声明
};

InfoMgr* InfoMgr::_spInst = nullptr; // 定义
InfoMgr::CGarbo Garbo;

总结:

饿汉特点:简单、初始化顺序不确定,如果有依赖关系就会有问题。饿汉对象初始化慢且多个饿汉单例对象会影响程序启动

懒汉特点:复杂一点、第一次调用时初始化,可以控制初始化顺序,延迟加载初始化,不影响程序启动

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

天影云光

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

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

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

打赏作者

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

抵扣说明:

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

余额充值