业务处理时常常会出现一些特定的要求,需要设计出特殊的功能,下面介绍几个常见的特殊类设计

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

涉及拷贝的只有拷贝构造赋值重载,把这些禁用了就行了

1
2
3
4
5
6
7
8
9
class CopyBan
{
// ...

private:
CopyBan(const CopyBan&);
CopyBan& operator=(const CopyBan&);
//...
};

对于 C++98,直接设置为私有即可,这样类外就无法访问了,只声明不定义:不定义是因为该函数根本不会调用,定义了其实也没有什么意义,不写反而还简单,而且如果定义了就不会防止成员函数内部拷贝了

1
2
3
4
5
6
7
8
class CopyBan
{
// ...
CopyBan(const CopyBan&) = delete;
CopyBan& operator=(const CopyBan&) = delete;
//...
};

对于 C++11,直接在默认成员函数后跟上 = delete,表示让编译器删除掉该默认成员函数

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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() 可直接通过类名调用

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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;
};

因为堆上创建销毁需要通过 newdelete,之前又学过 new -> operator new + 构造函数delete -> operator delete + 析构函数,所以直接将这些禁掉就好了

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

1
2
3
4
5
6
7
8
9
10
11
class NonInherit
{
public:
static NonInherit GetInstance()
{
return NonInherit();
}
private:
NonInherit()
{}
};

C++98 中构造函数私有化,派生类中调不到基类的构造函数,则无法继承

1
2
3
4
class A  final
{
// ....
};

final 关键字,final 修饰类,表示该类不能被继承

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

单例模式:

一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,这种方式简化了在复杂环境下的配置管理

以下是单例模式的两种实现模式:

🚩饿汉模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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,引发逻辑错误或崩溃

这类问题难以调试,且会间接增加启动阶段的异常处理开销

🚩懒汉模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
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;

在第一次使用时才初始化单例实例,而非程序启动时。这种方式避免了不必要的资源浪费,但需要处理多线程环境下的线程安全问题


希望读者们多多三连支持

小编会继续更新

你们的鼓励就是我前进的动力!

请添加图片描述