文章目录
一、禁止拷贝的类
在某些情况下,我们希望一个类的对象不能被拷贝,以确保对象的唯一性或避免不必要的复制操作。这在处理资源管理或某些特殊对象时非常有用。
C++ 98的实现方式
将拷贝构造函数与赋值运算符重载只声明不定义,并且将其访问权限设置为私有。
//禁止拷贝的类 C++98
class NonCopyablec98
{
private:
NonCopyablec98(const NonCopyablec98&);
NonCopyablec98& operator=(const NonCopyablec98);
public:
NonCopyablec98() {}
};
C++11的实现方式
C++11因为有 =delete 所以直接用就方便点,=delete来删除拷贝构造函数和赋值运算符重载。
//禁止拷贝的类 C++11
class NoCopyablec11
{
public:
NoCopyablec11() = default;
NoCopyablec11(const NoCopyablec11&) = delete;
NoCopyablec11& operator=(const NoCopyablec11&) = delete;
};
使用场景
假设我们有一个类 FileHandler,用于处理文件操作。每个文件只能被一个 FileHandler 对象打开和操作,如果允许拷贝,可能会导致多个对象同时操作同一个文件,从而引发问题。通过将 FileHandler 设计为禁止拷贝的类,可以确保文件的正确操作。
二、只能在堆上创建对象的类
有时,我们希望一个类的对象只能在堆上创建,这可以通过将类的构造函数私有,并提供一个静态的成员函数来创建堆对象来实现。
C++ 98的实现方式
//只能在堆上面创建对象的类 C++ 98
class HeapOnlyc98
{
public:
static HeapOnlyc98* CreateObject()
{
return new HeapOnlyc98;
}
private:
HeapOnlyc98() {}
HeapOnlyc98(const HeapOnlyc98&);
HeapOnlyc98& operator=(const HeapOnlyc98&);
};
C++11的实现方式
与 C++98 类似,但可以使用 =delete 来删除默认的构造函数
//只能在堆上面创建对象的类 C++ 11
class HeapOnly11
{
public:
static HeapOnly11* CreateObject()
{
return new HeapOnly11;
}
private:
HeapOnly11() = delete;
HeapOnly11(const HeapOnly11&) = delete;
HeapOnly11& operator=(const HeapOnly11&) = delete;
};
使用场景
考虑一个数据库连接池类 DBConnectionPool,它需要在堆上创建对象,以方便管理和共享连接资源。
三、只能在栈上创建对象的类
与只能在堆上创建对象的类相反,我们也可以设计一个只能在栈上创建对象的类。
C++98的实现
将构造函数私有化,然后设计静态方法创建对象返回。
class StackOnly98 {
public:
static StackOnly98 CreateObj() {
return StackOnly98();
}
private:
StackOnly98() {}
StackOnly98(const StackOnly98&);
StackOnly98& operator=(const StackOnly98&);
};
C++11的实现
与 C++98 类似,但可以使用 =delete 来删除 operator new 和 operator delete。
class StackOnly11 {
public:
static StackOnly11 CreateObj() {
return StackOnly11();
}
void* operator new(size_t size) = delete;
void operator delete(void* p) = delete;
private:
StackOnly11() {}
};
使用场景举例:
假设我们有一个线程局部存储类 ThreadLocalStorage,它需要在每个线程的栈上创建,以确保每个线程都有自己独立的存储区域。
四、不能被继承的类
在某些情况下,我们希望一个类不能被其他类继承,以保证类的完整性和特定的行为。
C++98的实现
将拷贝构造函数声明为私有,并且不提供实现。
class NonInherit98 {
public:
NonInherit98() {}
private:
NonInherit98(const NonInherit98&);
};
C++11的实现
使用 final 关键字来修饰类。
class NonInherit11 final {
public:
NonInherit11() {}
};
使用场景
考虑一个核心系统组件类 CoreComponent,它具有特定的功能和接口,并且不希望被其他类随意继承和修改,以确保系统的稳定性。
五、单例模式的类(只创建一个对象)
单例模式确保一个类只有一个实例,并提供一个全局访问点,常用于管理全局资源或共享状态。
使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 设计模式使代码编写真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。
饿汉模式
就是说不管你将来用不用,程序启动时就创建一个唯一的实例对象
//单例模式
//饿汉模式
class Singleton
{
public:
static Singleton* GenInstance()
{
return &m_instance;
}
private:
//构造函数私有保护起来
Singleton(){};
//C++ 98 防拷贝
Singleton(Singleton const&);
Singleton& operator=(Singleton const&);
//C++11 =delete防拷贝
Singleton(Singleton const&) = delete;
Singleton& operator=(Singleton const&) = delete;
static Singleton m_instance;
};
Singleton Singleton::m_instance;//在程序入口之前完成单例对象的初始化
优点:简单易于实现
缺点:如果有多个单例进行实现会导致进程启动慢
如果这个单例对象在多线程高并发环境下频繁使用,性能要求较高,那么显然使用饿汉模式来避免资源竞争,提高响应速度更好。
懒汉模式
如果单例对象构造十分耗时或者占用很多资源,比如加载插件啊, 初始化网络连接啊,读取
文件啊等等,而有可能该对象程序运行时不会用到,那么也要在程序一开始就进行初始化,
就会导致程序启动时非常的缓慢。 所以这种情况使用懒汉模式(延迟加载)更好。
// 懒汉模式
#include <mutex>
#include <thread>
class Singleton
{
public:
static Singleton* GetInstance() {
// 注意这里一定要使用Double-Check的方式加锁,才能保证效率和线程安全
if (nullptr == m_pInstance) {
m_mtx.lock();
if (nullptr == m_pInstance) {
m_pInstance = new Singleton();
}
m_mtx.unlock();
}
return m_pInstance;
}
// 实现一个内嵌垃圾回收类
class CGarbo {
public:
~CGarbo(){
if (Singleton::m_pInstance)
delete Singleton::m_pInstance;
}
};
// 定义一个静态成员变量,程序结束时,系统会自动调用它的析构函数从而释放单例对象
static CGarbo Garbo;
private:
// 构造函数私有
Singleton(){};
// 防拷贝
Singleton(Singleton const&);
Singleton& operator=(Singleton const&);
static Singleton* m_pInstance; // 单例对象指针
static mutex m_mtx; //互斥锁
};
Singleton* Singleton::m_pInstance = nullptr;
Singleton::CGarbo Garbo;
mutex Singleton::m_mtx;
int main()
{
thread t1([]{cout << &Singleton::GetInstance() << endl; });
thread t2([]{cout << &Singleton::GetInstance() << endl; });
t1.join();
t2.join();
cout << &Singleton::GetInstance() << endl;
cout << &Singleton::GetInstance() << endl;
return 0;
}
优点:第一次使用实例对象时,创建对象。进程启动无负载。多个单例实例启动顺序自由控制。
缺点:实现起来困难