涉及拷贝的只有拷贝构造
和赋值重载
,把这些禁用了就行了
class CopyBan
{
// ...
private:
CopyBan(const CopyBan&);
CopyBan& operator=(const CopyBan&);
//...
};
对于 C++98
,直接设置为私有即可,这样类外就无法访问了,只声明不定义:不定义是因为该函数根本不会调用,定义了其实也没有什么意义,不写反而还简单,而且如果定义了就不会防止成员函数内部拷贝了
class CopyBan
{
// ...
CopyBan(const CopyBan&) = delete;
CopyBan& operator=(const CopyBan&) = delete;
//...
};
对于 C++11
,直接在默认成员函数后跟上 = delete
,表示让编译器删除掉该默认成员函数
class HeapOnly
{
public:
static HeapOnly* CreateObject()
{
return new HeapOnly;
}
private:
HeapOnly() {}
// C++98
// 1.只声明,不实现。因为实现可能会很麻烦,而你本身不需要
// 2.声明成私有
HeapOnly(const HeapOnly&);
// or
// C++11
HeapOnly(const HeapOnly&) = delete;
};
创建对象需要构造函数,所以首先把构造函数给禁用了,还注意应该禁止拷贝构造,防止再创建,因为类内能调用构造函数,所以创建一个 public
函数提供接口
在构造函数被私有化的情况下,静态方法是唯一能够访问私有构造函数并创建对象的途径,静态方法属于类本身,而非类的某个实例,因此无需创建对象即可调用,例如: HeapOnly::CreateObject()
可直接通过类名调用
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;
};
因为堆上创建销毁需要通过 new
和 delete
,之前又学过 new -> operator new + 构造函数
,delete -> operator delete + 析构函数
,所以直接将这些禁掉就好了
class NonInherit
{
public:
static NonInherit GetInstance()
{
return NonInherit();
}
private:
NonInherit()
{}
};
C++98
中构造函数私有化,派生类中调不到基类的构造函数,则无法继承
class A final
{
// ....
};
final
关键字,final
修饰类,表示该类不能被继承
单例模式:
一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,这种方式简化了在复杂环境下的配置管理
以下是单例模式的两种实现模式:
🚩饿汉模式
class Singleton
{
public:
static Singleton* GetInstance()
{
return &m_instance;
}
private:
// 构造函数私有
Singleton() {};
// C++98 防拷贝
Singleton(Singleton const&);
Singleton& operator=(Singleton const&);
// or
// C++11
Singleton(Singleton const&) = delete;
Singleton& operator=(Singleton const&) = delete;
static Singleton m_instance;
};
Singleton Singleton::m_instance; // 在程序入口之前就完成单例对象的初始化
静态变量属于类而非对象,程序运行期间仅存在一份实例,且在程序入口(main()
)前初始化,static Singleton m_instance;
是声明,Singleton Singleton::m_instance;
是定义
饿汉模式在程序启动时(或类加载时)立即创建单例实例,若实例的初始化逻辑复杂(如: 读取大文件 / 配置(如数据库连接、配置文件) 网络请求(初始化远程服务连接) 复杂计算(预加载数据字典、缓存构建) 这些操作会阻塞进程的启动流程,导致启动时间明显增加
全局静态对象的初始化顺序问题:
在 C++
中,多个全局静态对象的初始化顺序是未定义的(由编译器和链接器决定),若某个饿汉单例 A
依赖另一个全局对象 B
,而 B
的初始化顺序在 A
之后,会导致 A
初始化时访问未初始化的 B
,引发逻辑错误或崩溃
这类问题难以调试,且会间接增加启动阶段的异常处理开销
🚩懒汉模式
class Singleton
{
public:
// 2、提供获取单例对象的接口函数
static Singleton& GetInstance()
{
if (_psinst == nullptr)
{
// 第一次调用GetInstance的时候创建单例对象
_psinst = new Singleton;
}
return *_psinst;
}
// 一般单例不用释放。
// 特殊场景:1、中途需要显示释放 2、程序结束时,需要做一些特殊动作(如持久化)
static void DelInstance()
{
if (_psinst)
{
delete _psinst;
_psinst = nullptr;
}
}
void Add(const pair<string, string>& kv)
{
_dict[kv.first] = kv.second;
}
void Print()
{
for (auto& e : _dict)
{
cout << e.first << ":" << e.second << endl;
}
cout << endl;
}
class GC
{
public:
~GC()
{
lazy::Singleton::DelInstance();
}
};
private:
// 1、构造函数私有
Singleton()
{
// ...
}
~Singleton()
{
cout << "~Singleton()" << endl;
// map数据写到文件中
FILE* fin = fopen("map.txt", "w");
for (auto& e : _dict)
{
fputs(e.first.c_str(), fin);
fputs(":", fin);
fputs(e.second.c_str(), fin);
fputs("\n", fin);
}
}
// 3、防拷贝
Singleton(const Singleton& s) = delete;
Singleton& operator=(const Singleton& s) = delete;
map<string, string> _dict;
// ...
static Singleton* _psinst;
static GC _gc;
};
Singleton* Singleton::_psinst = nullptr;
Singleton::GC Singleton::_gc;
在第一次使用时才初始化单例实例,而非程序启动时。这种方式避免了不必要的资源浪费,但需要处理多线程环境下的线程安全问题