• C++——特殊类设计


    目录

    一.不能被拷贝的类

    1.C++98做法

    2.C++11做法

    二.只能在堆上实例化的类

    1.实现方式一

    2.实现方式二

     三.只能在栈上创建的对象

    四.不能被继承的类

    1.C++98方式

    2.C++11方式

    五.只能创建一个对象的类

    1.设计模式

    2.单例模式


    一.不能被拷贝的类

    拷贝只会放在两个场景中:拷贝构造函数以及赋值运算符重载,因此想要让一个类禁止拷贝,
    只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。

    1.C++98做法

    将拷贝构造函数与赋值运算符重载只声明不定义,并且将其访问权限设置为私有即可。

    这样做的原因有两个:

    1. 只声明不定义:防止编译器自己生成拷贝构造函数以及赋值运算符重载,防止成员函数内部拷贝
    2. 设置成私有:防止别人在外部定义。
    1. class Bancopy
    2. {
    3. public:
    4. Bancopy(int x)
    5. :_x(x)
    6. {
    7. }
    8. private:
    9. Bancopy(Bancopy& obj);
    10. Bancopy& operator=(Bancopy& obj);
    11. private:
    12. int _x;
    13. };

    2.C++11做法

    C++11扩展delete的用法,delete除了释放new申请的资源外,如果在默认成员函数后跟上
    =delete,表示让编译器删除掉该默认成员函数。

    1. class Bancopy
    2. {
    3. public:
    4. Bancopy(int x)
    5. :_x(x)
    6. {
    7. }
    8. Bancopy(Bancopy& obj) = delete;
    9. Bancopy& operator=(Bancopy& obj) = delete;
    10. private:
    11. int _x;
    12. };

    二.只能在堆上实例化的类

    1.实现方式一

    1. 将析构函数私有,构造函数公有化。
    2. 凡是需要编译器自动调用析构函数释放的对象都是不可以创建的,只有堆空间创建的对象可以手动释放对象空间。
    3. 提供一个共有函数,Destory,用于对象的析构。 
    1. //思路一,将析构函数私有化
    2. class HeapOnly
    3. {
    4. public:
    5. HeapOnly(int x)
    6. :_x(x)
    7. {
    8. }
    9. void Destory()
    10. {
    11. cout << "delete this" << endl;
    12. delete this;
    13. }
    14. private:
    15. ~HeapOnly()
    16. {
    17. }
    18. private:
    19. int _x;
    20. };

     测试代码:

    1. int main()
    2. {
    3. //HeapOnly t1(100);//对象无法自动调用析构函数
    4. //static HeapOnly t2(100);//对象无法自动调用析构函数
    5. //手动释放对象空间
    6. HeapOnly* t = new HeapOnly(100);
    7. t->Destroy();
    8. return 0;
    9. }

    2.实现方式二

    1. 将类的构造函数私有,拷贝构造声明成私有。防止别人调用拷贝在栈上生成对象。
    2. 提供一个静态的成员函数,在该静态成员函数中完成堆对象的创建。

    1. //思路二,将构造函数私有化
    2. class HeapOnly
    3. {
    4. public:
    5. static HeapOnly* CreateObj(int x)
    6. {
    7. return new HeapOnly(x);
    8. }
    9. void Destroy()
    10. {
    11. cout << "delete this" << endl;
    12. delete this;
    13. }
    14. HeapOnly(HeapOnly& Obj) = delete;
    15. HeapOnly& operator=(HeapOnly& Obj) = delete;
    16. private:
    17. HeapOnly(int x)
    18. :_x(x)
    19. {
    20. }
    21. private:
    22. int _x;
    23. };

    测试代码:

    1. int main()
    2. {
    3. //手动释放对象空间
    4. HeapOnly* t = HeapOnly::CreateObj(100);
    5. //HeapOnly t2 = (*t); error
    6. //t2 = *t; error
    7. t->Destroy();
    8. return 0;
    9. }

     三.只能在栈上创建的对象

    方法一:同上将构造函数私有化,然后设计静态方法创建对象返回即可。

    1. class StackOnly
    2. {
    3. public:
    4. static StackOnly CreateObj(int x)
    5. {
    6. return StackOnly(x);
    7. }
    8. StackOnly& operator=(StackOnly& Obj) = delete;
    9. private:
    10. StackOnly(int x)
    11. :_x(x)
    12. {
    13. }
    14. private:
    15. int _x;
    16. };

    缺陷:对于静态对象我们没有办法阻止创建,因为我们通过静态的CreateObj也是需要传值返回的。如果将拷贝构造封住,换成移动构造,可以将普通的对象赋值给静态的对象拦截住,但是对于move之后的普通对象赋值给静态对象。

    1. class StackOnly
    2. {
    3. public:
    4. static StackOnly CreateObj(int x)
    5. {
    6. return StackOnly(x);
    7. }
    8. StackOnly(StackOnly&& Obj)
    9. {
    10. _x = Obj._x;
    11. }
    12. StackOnly(StackOnly& Obj) = delete;
    13. StackOnly& operator=(StackOnly& Obj) = delete;
    14. private:
    15. StackOnly(int x)
    16. :_x(x)
    17. {
    18. }
    19. private:
    20. int _x;
    21. };
    1. int main()
    2. {
    3. StackOnly t = StackOnly::CreateObj(100);
    4. //StackOnly* pt = new StackOnly(200); error
    5. //static StackOnly t1 = t; //error
    6. static StackOnly t1 = move(t);//没办法阻止
    7. return 0;
    8. }

    四.不能被继承的类

    1.C++98方式

    C++98中构造函数私有化,派生类中调不到基类的构造函数。则无法继承。派生类在对于基类的那部分数据回去调用基类的构造函数来完成,如果基类的构造函数变成私有,那么派生类就无法调用构造。接入派生类无法创建对象。

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

    2.C++11方式

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

    1. class base final
    2. {
    3. public:
    4. base()
    5. {}
    6. private:
    7. };

    五.只能创建一个对象的类

    1.设计模式

    设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的
    总结
    。为什么会产生设计模式这样的东西呢?就像人类历史发展会产生兵法。最开始部落之间打
    仗时都是人拼人的对砍。后来春秋战国时期,七国之间经常打仗,就发现打仗也是有套路的,后
    来孙子就总结出了《孙子兵法》。孙子兵法也是类似。
    使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 设计模
    式使代码编写真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。

    2.单例模式

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

    • 饿汉模式

    就是说不管你将来用不用,程序启动时就创建一个唯一的实例对象。

    可能会导致进程启动慢,且如果有多个单例类对象实例启动顺序不确定。

    1. //单例模式——饿汉模式
    2. class Singleton
    3. {
    4. public:
    5. static Singleton* GetSingleton()
    6. {
    7. return singleton;
    8. }
    9. vector &get_sing_list()
    10. {
    11. return _sing_list;
    12. }
    13. void showlist()
    14. {
    15. for (auto e : _sing_list)
    16. {
    17. cout << e << endl;
    18. }
    19. }
    20. private:
    21. //构造函数
    22. Singleton()
    23. {}
    24. Singleton(Singleton const&) = delete;
    25. Singleton& operator=(Singleton const&) = delete;
    26. private:
    27. vector _sing_list;
    28. static Singleton* singleton;
    29. };
    30. //初始化的是类内部成员,可以访问私有的构造函数
    31. //在没有进入主函数的时候,单例对象已经实例化完成
    32. Singleton* Singleton::singleton = new Singleton;

     测试代码:

    1. int main()
    2. {
    3. Singleton* singleton = Singleton::GetSingleton();
    4. singleton->get_sing_list().push_back("《冰雨》——刘德华");
    5. singleton->get_sing_list().push_back("《月光》——胡彦斌");
    6. singleton->get_sing_list().push_back("《倒带》——周杰伦");
    7. singleton->get_sing_list().push_back("《呓语》——毛不易");
    8. singleton->showlist();
    9. return 0;
    10. }

    如果这个单例对象在多线程高并发环境下频繁使用,性能要求较高,那么显然使用饿汉模式来避
    免资源竞争,提高响应速度更好。

    • 懒汉模式

    如果单例对象构造十分耗时或者占用很多资源,比如加载插件啊, 初始化网络连接啊,读取
    文件啊等等,而有可能该对象程序运行时不会用到,那么也要在程序一开始就进行初始化,
    就会导致程序启动时非常的缓慢。 所以这种情况使用懒汉模式(延迟加载)更好。

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

    1. //单例模式——懒汉模式
    2. class Singleton
    3. {
    4. public:
    5. static Singleton* GetSingleton()
    6. {
    7. if (singleton == nullptr)//防止因为每次创建对象都要加锁带来的性能上的损耗
    8. {
    9. _mutex.lock();//防止多线程创建的时候,出现并发问题
    10. if (singleton == nullptr)
    11. {
    12. singleton = new Singleton;
    13. }
    14. _mutex.unlock();
    15. }
    16. return singleton;
    17. }
    18. vector &get_sing_list()
    19. {
    20. return _sing_list;
    21. }
    22. void showlist()
    23. {
    24. for (auto e : _sing_list)
    25. {
    26. cout << e << endl;
    27. }
    28. }
    29. private:
    30. //构造函数
    31. Singleton()
    32. {
    33. i++;
    34. }
    35. Singleton(Singleton const&) = delete;
    36. Singleton& operator=(Singleton const&) = delete;
    37. private:
    38. static mutex _mutex;
    39. vector _sing_list;
    40. static Singleton* singleton ;
    41. };
    42. Singleton* Singleton::singleton = nullptr;
    43. mutex Singleton::_mutex;

  • 相关阅读:
    dos命令bat结合任务计划程序自动执行py文件
    【Godot引擎开发】简单基础,外加一个小游戏DEMO
    web综合案例-day01
    原厂原装 应广单片机PMS134方案开发应用案例
    idea修改颜色
    opencv语法Mat类型总结
    汇编原理 | 二进制、跳转指令、算数运算、
    maven命令上传文件到私服deploy-file
    【SpringBoot整合NoSql】-----Redis篇
    go类型转换,整形转字符串,浮点型转字符串,字符串到float
  • 原文地址:https://blog.csdn.net/qq_63943454/article/details/133970454