特殊类设计 Shepard-Wang

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

/*
 * 只能在堆上创建对象
 */
class HeapOnly
{
public:
	static HeapOnly* getInstance()
	{
		return new HeapOnly;
	}

private:
	HeapOnly()
	{}

	// 拷贝构造应该只声明不实现,避免在类的 public 函数中调用从而在
	// 函数栈中构造对象
	HeapOnly(const HeapOnly& hp);
};

int main(void)
{
    // 通过调用 类名::static函数 创建堆上对象
    // 不能直接使用 hp->getInstance(),因为 hp 还没有被构造出来
	HeapOnly* hp = HeapOnly::getInstance();

	return 0;
}

另一种方法:将析构函数设置为私有

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

副作用是 new 出来的对象也没法 delete

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

/*
 * 只能在栈上构造对象
 */
class StackOnly
{
private:
	StackOnly()
	{}
public:
	static StackOnly getInstance()
	{
		return StackOnly();
	}
};

int main(void)
{
	StackOnly so = StackOnly::getInstance();

	return 0;
}

另一种思路:

class StackOnly
{
private:
    // 禁掉 new 防止用户在堆上声明对象
	void* operator new(size_t size);
	// 或者写作:void* operator new(size_t size) = delete;

	// 构造函数必须设为私有,防止用户声明全局变量(数据段)
	StackOnly()
	{}
public:
	static StackOnly getInstance()
	{
		return StackOnly();
	}
};

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

class NoCopy
{
public:
	NoCopy()
	{}
	
	//NoCopy(const NoCopy& nc) = delete;
	//NoCopy& operator=(const NoCopy& nc) = delete;
private:
	// 声明为私有且不实现 或者定义为 delete 函数都可
	NoCopy(const NoCopy& nc);
	NoCopy& operator=(const NoCopy& nc);
};

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

方法 1

final 定义的类不能被继承

class NoInhert final{};

class Sub : NoInhert{}; // Error!

方法 2

将父类的构造函数声明为私有

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

两种方式,饿汉模式懒汉模式

/*
 * 单例模式
 * 1. 饿汉模式:
 *    1) 构造函数私有
 *	  2) 提供一个静态的方法返回单例
 *	  3) 提供一个静态单例成员
 *    4) 拷贝构造和赋值运算符重载声明为 delete 函数
 *	优势:
 *		实现简单,多线程情况下效率高
 *  缺点:
 *		程序启动慢,多个单例对象的初始化顺序无法控制
 *  
 * 2. 懒汉模式
 *		调用接口时再创建对象,延迟加载(饿汉模式直接先创建对象)
 *  优势:
 *		程序启动快,多个单例对象的的初始化顺序可以控制
 *	缺点:
 *		实现复杂,需要考虑多线程问题
 */
// 饿汉模式
class SingleTon1
{
private:
	static SingleTon1 st;

	SingleTon1() {};

	// 禁掉拷贝构造和赋值运算符重载
	SingleTon1(const SingleTon1& st) = delete;
	SingleTon1& operator=(const SingleTon1& st) = delete;

public:
	static SingleTon1* GetInstance()
	{
		return &st;
	}
};

// 静态成员初始化
SingleTon1 SingleTon1::st;

// 懒汉模式:需要注意线程安全问题
class SingleTon2
{
private:
	static SingleTon2* pst;
	static mutex _mtx;

	SingleTon2() {};
	
	SingleTon2(const SingleTon2& st) = delete;
	SingleTon2& operator=(const SingleTon2& st) = delete;

public:
	static SingleTon2* GetInstance()
	{
		// 因为创建对象仅在第一次,而上锁解锁会严重降低效率,
		// 所以我们只用在创建对象时上锁即可
		if (pst == nullptr)
		{
			_mtx.lock();
			if (pst == nullptr)
			{
				pst = new SingleTon2;
			}
			_mtx.unlock();
		}

		return pst;
	}
};

SingleTon2* SingleTon2::pst = nullptr;
mutex SingleTon2::_mtx;

void test1()
{
	cout << "test 饿汉模式:" << endl;
	for (int i = 0; i < 10; i++)
	{
		cout << (void*)SingleTon1::GetInstance() << endl;
	}

	cout << "test 懒汉模式:" << endl;
	for (int i = 0; i < 10; i++)
	{
		cout << (void*)SingleTon2::GetInstance() << endl;
	}

}