C++——特殊类设计

目录

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

2、请设计一个类,只能在堆上创建对象

1.封构造函数

2.封析构函数

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

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

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

单例模式概念

饿汉模式

缺点

懒汉模式


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


// C++98
// 只声明,不实现
class CopyBan
{
// ...
private:
CopyBan(const CopyBan&);
CopyBan& operator=(const CopyBan&);
//...
};

// C++11
// delete

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

2、请设计一个类,只能在堆上创建对象

赋值重载不用禁掉,因为赋值肯定是给已经存在的对象进行赋值。
既然已经不能在栈上创建对象了,那么就算赋值也是赋值的堆上的对象。

1.封构造函数


class HeapOnly
{
public:
	// 这里必须写成静态的
	// 当我们把构造给写成私有的了,就不能构造出对象了
	// 因此只能通过类调用该函数
	// 将它设为static让它属于整个类
	static HeapOnly* CreateObj()
	{
		return new HeapOnly;
	}

private:
	HeapOnly() // 设为私有,禁止外部构造
	{}

	// 禁止拷贝构造
	HeapOnly(const HeapOnly& hp) = delete;
};

int main()
{
	HeapOnly* hp = HeapOnly::CreateObj();

	//HeapOnly hp1(*hp); // 拷贝构造禁止之后这句就要报错

	return 0;
}

2.封析构函数


//封析构函数
class HeapOnly
{
public:
	HeapOnly()
	{}

	// 由于析构设为了私有
	// 所以要自己实现一个接口来调用它
	void Destroy()
	{
		this->~HeapOnly();
	}
private:
	// 将析构设为私有
	~HeapOnly() 
	{}

	// 禁止拷贝构造
	HeapOnly(const HeapOnly& hp) = delete;
};

int main()
{
	//HeapOnly hp1;

	HeapOnly* php1 = new HeapOnly;

	//可以直接用对象来调用Destroy
	php1->Destroy();

	return 0;
}

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


//只能在栈上创建对象

class StackOnly
{
public:
	// 调用构造函数
	static StackOnly CreateObj()
	{
		return StackOnly(); 
	}

	void Print()
	{
		cout << "StackOnly::Print()" << endl;
	}
private:

	// 将构造设为私有
	StackOnly()
	{}

};

int main()
{
	// 仍然可以调用CeateObj来在栈上创建对象
	StackOnly so1 = StackOnly::CreateObj();

	// 但是防不住生成静态对象
	// static StackOnly so4 = StackOnly::CreateObj();

	StackOnly::CreateObj().Print();

	const StackOnly& so4 =  StackOnly::CreateObj();
	so4.CreateObj();

	// 这样就不能创建静态的对象了
	// static StackOnly so2;
	// 也不能在堆上创建对象了
	// StackOnly*pso3=new StackOnly;


	return 0;
}

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


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



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

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

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

单例模式概念

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

饿汉模式


//单例模式的类:全局只有一个唯一对象
//饿汉模式: main函数执行之前就创建对象


class InfoSingleton
{
public:

	static InfoSingleton& GetInstance()
	{
		//返回我们在类内创建的对象就可以了
		return _sins;
	}

	void Insert(string name, int salary)
	{
		_info[name] = salary;

	}

	void Print()
	{
		for (auto kv : _info)
		{
			cout << kv.first << " " << kv.second << endl;
		}
	}
private:
	// 为了避免允许创建多个对象
	// 因此需要把构造函数给封了 
	InfoSingleton()
	{}

	//拷贝构造和赋值也要设为私有,以防通过拷贝和赋值搞出新对象

	InfoSingleton(const InfoSingleton& info);
	InfoSingleton& operator=(const InfoSingleton& info);

	map _info;



	//在类内定义了一个类类型的
private:
	// 不能在类外调用构造函数,但是可以再类里面调用
	static InfoSingleton _sins;
};

// 第一个是类型 第二个是作用域
InfoSingleton InfoSingleton::_sins;

int main()
{
	InfoSingleton::GetInstance().Insert("张三", 10000);

	InfoSingleton& infosl = InfoSingleton::GetInstance();

	infosl.Insert("李四", 15000);
	infosl.Insert("赵六", 12000);
	infosl.Insert("王五", 15000);
	infosl.Insert("李四", 15000);

	infosl.Print();

	//InfoSingleton copy = InfoSingleton::GetInstance();

	//copy.Insert("孙7", 50000);
	//copy.Print();

	//infosl.Print();

	return 0;
}

注意:

注意:
主要就是在类中定义的那个静态的类类型的对象,使外部可以使用类域来调用类内这个对象,并且可以调用该类的构造函数。

缺点

1、单例对象初始化时数据太多,导致启动慢,因为它在main函数之前就要初始化。

2、多个单例类有初始化依赖关系,饿汉模式无法控制

举例:

A和B都是单例类,要求先初始化A,再初始化B,因为B会依赖A

但是饿汉模式无法控制顺序

懒汉模式


// 使用RAII的锁管理方式
//在这里我们手写了一个,其实库里面是有这个的
template
class LockGuard
{
public:
	LockGuard(Lock& lk)
		:_lk(lk)
	{
		_lk.lock();
	}
	~LockGuard()
	{
		_lk.unlock();
	}
private:
	// 引用的成员变量必须在初始化列表进行初始化
	mutex& _lk;
};

//懒汉模式:第一次获取单例对象的时候创建对象
//1、对象再main函数之后才会创建,不会影响启动顺序
//2、可以主动控制创建顺序

class InfoSingleton
{
public:
	// 多个线程一起调用GetInstance,存在线程安全的风险
	// 所以需要加锁
	static InfoSingleton& GetInstance()
	{
		//第一次获取单例对象的时候创建对象
		//双检查加锁

		//对象new出来以后,避免每次都加锁的检查,可以提高性能
		if (_psins == nullptr)
		{
			// 使用RAII的锁管理方式
			LockGuard lock(_smtx);
			//这个是库里面的
			//std::lock_guard lock(_smtx);
			// 为了保证线程安全且只new一次
			if (_psins == nullptr) 
			{
				_psins = new InfoSingleton;
			}

		}


		return *_psins;
	}

	//一般单例对象不需要考虑释放
	//单例对象不用时,必须手动处理,一些资源需要保存
	//可以手动调用主动回收
	//也可以让他自己在程序结束时,自动回收


	//可以手动调用主动回收
	static void DelInstance()
	{
		// 保存数据到文件
		// ...
		lock_guard lock(_smtx);
		if (_psins)
		{
			delete _psins;
			_psins = nullptr; 
		}
	}


	//也可以让他自己在程序结束时,自动回收
	//内部类
	class GC
	{
	public:
		~GC()
		{
			//内部类是外部类的友元
			//因此我们可以直接调用
			if (_psins)
			{
				cout << "~GC()" << endl;
				DelInstance();
			}
			
		}

	};


	void Insert(string name, int salary)
	{
		_info[name] = salary;

	}

	void Print()
	{
		for (auto kv : _info)
		{
			cout << kv.first << " : " << kv.second << endl;
		}
	}
private:
	// 为了避免允许创建多个对象
	// 因此需要把构造函数给封了 
	InfoSingleton()
	{}

	//拷贝构造和赋值也要设为私有,以防通过拷贝和赋值搞出新对象

	InfoSingleton(const InfoSingleton& info);
	InfoSingleton& operator=(const InfoSingleton& info);

	map _info;



	//在类内定义了一个类类型的
private:
	// 不能在类外调用构造函数,但是可以再类里面调用
	static InfoSingleton* _psins;
	static mutex _smtx;
	// 这个局部对象出了作用域救护调用对应的析构函数
	static GC _gc;
};

// 第一个是类型 第二个是作用域
InfoSingleton* InfoSingleton::_psins = nullptr;
mutex InfoSingleton::_smtx;
InfoSingleton::GC InfoSingleton::_gc;

int main()
{
	InfoSingleton::GetInstance().Insert("张三", 10000);

	InfoSingleton& infosl = InfoSingleton::GetInstance();

	infosl.Insert("李四", 15000);
	infosl.Insert("赵六", 12000);
	infosl.Insert("王五", 15000);
	infosl.Insert("李四", 15000);

	infosl.Print();

	//InfoSingleton copy = InfoSingleton::GetInstance();

	//copy.Insert("孙7", 50000);
	//copy.Print();

	//infosl.Print();

	InfoSingleton::DelInstance();

	return 0;
}

你可能感兴趣的:(C++,c++,开发语言)