news 2026/4/29 20:49:35

C++特殊类设计概念与示例讲解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++特殊类设计概念与示例讲解

一、设计模式概念

设计模式是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。

使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。

根本原因是为了代码复用,增加可维护性。

设计模式的例子:迭代器模式

二、设计一个不能被拷贝的类

拷贝一共就只有两个场景,一个是拷贝构造,一个是赋值运算符重载。所以我们想要设计出一个不能被拷贝的类只需要让外部无法调用这两个函数即可。

在C++98中,我们的方法是将拷贝构造和赋值运算符重载只声明不定义并且将权限设置为私有。

1

2

3

4

5

6

7

8

9

classanti_copy

{

public:

anti_copy()

{}

private:

anti_copy(constanti_copy& ac);

anti_copy& operator=(constanti_copy& ac);

};

设计原因:

1️⃣ 私有:如果声明成共有,那么就可以在类外面实现定义。

2️⃣ 只声明不定义:因为如果不声明编译器会默认生成这两个的默认成员函数。而不定义是因为该函数不会被调用,就不用写了,这样编译的时候就会出现链接错误。

而在C++11中引入了关键字——delete。

如果在默认成员函数后跟上=delete,表示让编译器删除掉该默认成员函数。即使权限是共有也无法调用已删除的函数。

1

2

3

4

5

6

7

8

9

classanti_copy

{

public:

anti_copy()

{}

anti_copy(constanti_copy& ac) =delete;

anti_copy& operator=(constanti_copy& ac) =delete;

private:

};

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

3.1 私有构造

首先要把构造函数给私有,不然这个类就可以在任意位置被创建。而构造函数被私有了以后我们怎么创建对象呢?

我们可以在定义一个成员函数,让这个函数在堆上申请空间,但我们知道必须现有对象才能调用成员函数。所以我们就把这个函数设置成静态成员函数。

1

2

3

4

5

6

7

8

9

10

11

classOnlyHeap

{

public:

staticOnlyHeap* GetObj()

{

returnnewOnlyHeap;

}

private:

OnlyHeap()

{}

};

但是这样也不完全对,如果我们这么写:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

classOnlyHeap

{

public:

staticOnlyHeap* GetObj()

{

returnnewOnlyHeap;

}

private:

OnlyHeap()

{}

};

intmain()

{

OnlyHeap* hp1 = OnlyHeap::GetObj();

OnlyHeap hp2(*hp1);

return0;

}

这里的hp2就是栈上的对象。所以我们也要把拷贝构造给封住。

1

2

3

4

5

6

7

8

9

10

11

12

classOnlyHeap

{

public:

staticOnlyHeap* GetObj()

{

returnnewOnlyHeap;

}

OnlyHeap(constOnlyHeap& hp) =delete;

private:

OnlyHeap()

{}

};

3.2 私有析构

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

classOnlyHeap

{

public:

OnlyHeap()

{}

OnlyHeap(constOnlyHeap& hp) =delete;

private:

~OnlyHeap()

{}

};

intmain()

{

OnlyHeap hp1;// error

OnlyHeap* hp2 =newOnlyHeap;

return0;

}

这里的hp1就不能创建成功,因为对象销毁的时候会调用析构函数,但是这里的析构是私有的,所以该对象无法调用。

但是我们要销毁hp2该怎么办呢?

我们可以定义一个成员函数显示调用析构函数。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

classOnlyHeap

{

public:

OnlyHeap()

{}

OnlyHeap(constOnlyHeap& hp) =delete;

voidDestroy()

{

this->~OnlyHeap();

}

private:

~OnlyHeap()

{}

};

intmain()

{

OnlyHeap* hp2 =newOnlyHeap;

hp2->Destroy();

return0;

}

四、设计一个只能在栈上创建对象的类

为了不让这个类随便定义出对象,首先要把构造函数私有。然后跟上面只能在堆上创建对象的方法相似,定义出一个静态成员函数返回栈上创建的对象。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

classStackOnly

{

public:

staticStackOnly GetObj()

{

returnStackOnly();

}

private:

StackOnly()

{}

};

intmain()

{

StackOnly hp = StackOnly::GetObj();

return0;

}

但是这里有一个问题,无法防止创建静态对象:

1

staticStackOnly hp2 = StackOnly::GetObj();

五、设计不能被继承的类

在C++98,为了不让子类继承,我们可以把构造函数私有化,因为子类需要先调用父类的构造函数初始化父类的那一部分成员。

1

2

3

4

5

6

7

classNoInherit

{

public:

private:

NoInherit()

{}

};

而在C++11中引入的新的关键字final,被final关键字修饰的类不能被继承。

1

2

3

4

5

classNoInherit final

{

public:

private:

};

六、单例模式

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

单例模式的特点就是全局只有一个唯一对象。

6.1 饿汉模式

怎么能做到全局只是用一个对象呢,比方说我们现在想要实现一个英汉字典,首先我们要把构造函数私有,不然无法阻止创建对象。然后我们可以在类里面定义一个自己类型的静态成员变量,作用域是全局的。因为对比定义在外边的静态成员变量,内部的可以调用构造函数。

这里要注意把拷贝也要封住。

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

classSingleton

{

public:

staticSingleton& GetObj()

{

return_s;

}

voidinsert(conststd::string& s1,conststd::string& s2)

{

_dict[s1] = s2;

}

voidPrint()

{

for(auto& e : _dict)

{

cout << e.first <<"->"<< e.second << endl;

}

}

// 防拷贝

Singleton(constSingleton&) =delete;

Singleton& operator=(constSingleton&) =delete;

private:

Singleton()

{}

std::map<std::string, std::string> _dict;

private:

staticSingleton _s;// 声明

};

Singleton Singleton::_s;// 定义

intmain()

{

Singleton::GetObj().insert("corn","玉米");

Singleton& dic1 = Singleton::GetObj();

dic1.insert("apple","苹果");

dic1.insert("banana","香蕉");

Singleton& dic2 = Singleton::GetObj();

dic2.insert("pear","梨");

dic2.Print();

return0;

}

饿汉模式有什么特点呢?

它会在一开始(main之前)就创建对象。

饿汉模式有什么缺点呢?

1️⃣ 如果单例对象构造十分耗时或者占用很多资源,比如加载插件啊, 初始化网络连接啊,读取文件啊等等,而有可能该对象程序运行时不会用到,那么也要在程序一开始就进行初始化,就会导致程序启动时非常的缓慢。

2️⃣ 多个单例类之间如果有依赖关系饿汉模式就无法控制,比方说要求A类初始化时必须调用B,但是饿汉无法控制先后顺序。

所以针对这些问题,就有了懒汉模式。

6.2 懒汉模式

第一次使用实例对象时,创建对象(用的时候创建)。进程启动无负载。多个单例实例启动顺序自由控制。

我们可以直接对上面饿汉模式的代码进行修改,把静态成员变量变成指针。然后把获取的函数改变一下:

1

2

3

4

5

6

7

8

9

staticSingleton& GetObj()

{

// 第一次调用才会创建对象

if(_s == nullptr)

{

_s =newSingleton;

}

return*_s;

}

整体代码:

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

classSingleton

{

public:

staticSingleton& GetObj()

{

// 第一次调用才会创建对象

if(_s == nullptr)

{

_s =newSingleton;

}

return*_s;

}

voidinsert(conststd::string& s1,conststd::string& s2)

{

_dict[s1] = s2;

}

voidPrint()

{

for(auto& e : _dict)

{

cout << e.first <<"->"<< e.second << endl;

}

}

// 防拷贝

Singleton(constSingleton&) =delete;

Singleton& operator=(constSingleton&) =delete;

private:

Singleton()

{}

std::map<std::string, std::string> _dict;

private:

staticSingleton* _s;// 声明

};

Singleton* Singleton::_s = nullptr;// 定义

6.2.1 线程安全问题

上面的代码存在问题,当多个线程同时调用GetObj(),就会创建多个对象。所以为了线程安全我们要加锁。为了保证锁自动销毁,我们可以自定义一个锁。

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

template<classLock>

classLockAuto

{

public:

LockAuto(Lock& lk)

: _lk(lk)

{

_lk.lock();

}

~LockAuto()

{

_lk.unlock();

}

private:

Lock& _lk;

};

classSingleton

{

public:

staticSingleton& GetObj()

{

// 第一次调用才会创建对象

if(_s == nullptr)// 只有第一次才用加锁

{

LockAuto<mutex> lock(_mutex);

if(_s == nullptr)

{

_s =newSingleton;

}

}

return*_s;

}

voidinsert(conststd::string& s1,conststd::string& s2)

{

_dict[s1] = s2;

}

voidPrint()

{

for(auto& e : _dict)

{

cout << e.first <<"->"<< e.second << endl;

}

}

// 防拷贝

Singleton(constSingleton&) =delete;

Singleton& operator=(constSingleton&) =delete;

private:

Singleton()

{}

std::map<std::string, std::string> _dict;

private:

staticSingleton* _s;// 声明

staticmutex _mutex;// 锁

};

Singleton* Singleton::_s = nullptr;// 定义

mutex Singleton::_mutex;// 定义

6.2.2 新写法

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

classSingleton

{

public:

staticSingleton& GetObj()

{

staticSingleton dic;

returndic;

}

voidinsert(conststd::string& s1,conststd::string& s2)

{

_dict[s1] = s2;

}

voidPrint()

{

for(auto& e : _dict)

{

cout << e.first <<"->"<< e.second << endl;

}

}

// 防拷贝

Singleton(constSingleton&) =delete;

Singleton& operator=(constSingleton&) =delete;

private:

Singleton()

{}

std::map<std::string, std::string> _dict;

};

这里就用了静态局部变量只会在第一次定义的时候初始化。在C++11之前是不能保证线程安全的,但是C++11之后就可以了。


版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/29 20:48:45

题解:CF393B Three matrices

这是一道简单的模拟题 思路 我们先把 WWW 矩阵读入进来。 然后根据题意,对 W,A,BW,A,BW,A<

作者头像 李华
网站建设 2026/4/29 20:40:40

Altium Develop 正式上线:功能亮点与试用指南

3月16日&#xff0c;Altium 正式在中国市场发布了新一代电子研发协同平台 Altium Develop。在当下的电子设计环境中&#xff0c;团队协作、信息实时同步和设计数据安全&#xff0c;已经不再是“锦上添花”&#xff0c;而是决定研发效率和交付质量的关键基础。 《Altium 在中国…

作者头像 李华
网站建设 2026/4/29 20:38:30

魔兽争霸3的“时光机“:让经典游戏在现代电脑上重生

魔兽争霸3的"时光机"&#xff1a;让经典游戏在现代电脑上重生 【免费下载链接】WarcraftHelper Warcraft III Helper , support 1.20e, 1.24e, 1.26a, 1.27a, 1.27b 项目地址: https://gitcode.com/gh_mirrors/wa/WarcraftHelper 还记得那个在网吧通宵达旦的夜…

作者头像 李华
网站建设 2026/4/29 20:36:26

CICD 信发系统自动打包智能体—无相无界(8)—东方仙盟

角色与规则 你是多平台持续打包 CI/CD 配置生成助手&#xff0c;当前优先适配安卓项目&#xff0c;后续可扩展其他平台。所有配置均使用已线上验证、可正常构建的固定模板&#xff0c;禁止修改模板底层代码、打包逻辑、签名逻辑、仓库防护配置&#xff0c;仅做变量替换与内容输…

作者头像 李华
网站建设 2026/4/29 20:32:32

Beyond Compare 5密钥生成器:快速激活与授权管理完整指南

Beyond Compare 5密钥生成器&#xff1a;快速激活与授权管理完整指南 【免费下载链接】BCompare_Keygen Keygen for BCompare 5 项目地址: https://gitcode.com/gh_mirrors/bc/BCompare_Keygen Beyond Compare 5作为专业文件对比工具&#xff0c;其30天评估期限制常常让…

作者头像 李华