当前位置: 首页 > news >正文

C++单例模式与特殊类的设计

文章目录

    • 设计模式
    • 特殊类的设计
      • 不能被拷贝的类
      • 不能被继承的类
      • 只在堆区创建的类
      • 只在栈区创建的类
    • 单例模式
      • 饿汉模式
      • 懒汉模式

设计模式

我们以前学的面向过程、面向对象,还有封装、继承、多态

这些就像是武功里面的内功,练到了会用了就是会,而不会想学也都是从0到1,一步步学习练习

而设计模式就像是武功里的外功,属于是学会了,但是要怎么样才能把这个功力的作用最大化,在哪些条件下用什么样的内功能破解,研究的是这个问题

例如,我想设计一个只能在栈或堆上实例化的类应该怎么做,我想设计一个不能被拷贝或继承的类应该怎么做,这些就是外功,是有固定章法的

但是在C++中,设计模式关注的内容没有那么多那么全,我们暂时只了解单例模式即可

特殊类的设计

不能被拷贝的类

在C++11中,引入了delete关键字,可以直接禁用拷贝构造和赋值重载

在C++98中,虽然没有这个关键字,但是我们可以直接把类的拷贝构造和赋值重载设置为私有即可

// C++11
class A {
public:A(int x = 1) :num(x) {};A(const A& a) = delete;A& operator=(const A& a) = delete;
public:int num;
};
// C++98
class A {
public:A(int x = 1) :num(x) {}
public:int num;
private:A(const A& a) {}A& operator=(const A& a) {}
};

需要注意的是拷贝构造也属于构造函数,当我们写了拷贝构造,就不会生成默认构造函数了,需要自己写

不能被继承的类

在C++11中,直接在类后面加final就能解决问题

在C++98中,将构造函数私有化也能达到目的,因为子类在构造的时候会调用基类的构造

但是这样会有一个问题,就是没办法直接进行实例化

我们可以借助静态成员函数来进行访问

// C++11
class A final
{
public:A() {};
}// C++98
class A
{
public:static A GetA() {return A();}
private:A() {};
}

我们可以这样来创建A的实例

A a = A::GetA();

同时也实现了A不能被继承的特性

这是静态成员啊还能输的特性,但是静态成员啊还能输不能之际范围跟非静态成员或者非静态成员函数,并且他没有自己的this指针,本质上是静态区的内容

只在堆区创建的类

这种类的作用是防止对象被拷贝或赋值,只能通过指针传递

好处就是当类特别大时防止栈溢出,其次就是在一些设计模式中可能会用到

如果是采用指针进行管理,需要注意防止多重释放造成的内存泄漏问题

第一种思路就是,禁止直接使用构造函数,只能通过我给你的静态成员函数接口调用,而且必须设置为静态的,如果是非静态成员函数,就首先需要实例化一个对象才能调用,于是就循环调用了

class A {
public:static A* GetA_Heap() {return new A;}
private:A(int x = 1) :num(x) {};A(const A& a) = delete;A& operator=(const A& a) = delete;
public:int num;
};

这里同样是需要将拷贝构造和赋值重载禁用的,为了避免意外的拷贝和赋值操作导致栈上也创建

同样的道理,私有化析构函数也能达到这个目的,也同样需要些一个静态成员函数提供接口

只在栈区创建的类

同样将构造函数私有化,只暴露接口给外面调用即可

需要注意的是,禁用new、new[]、delete、delete[]

class A {
public:static A GetA_Stack() {return A();}
private:A(int x = 1) :num(x) {};void* operator new(size_t size) = delete;void* operator new[](size_t size) = delete;void operator delete(void* ptr) = delete;void operator delete[](void* ptr) = delete;
public:int num;
};

单例模式

单例模式意思是只允许实例化一个对象的类

单例模式也有不同的实现思路,分别称之为饿汉模式和懒汉模式

饿汉模式指的是在类加载时就创建实例,因此线程安全,但也会导致一些不必要的资源消耗,读条时间会变久

懒汉模式指的是在首次使用时才创建实例,这样可以节省很多资源,但在多线程环境中需要注意线程安全的问题,需要引入互斥锁解决

饿汉模式

怎么样实现在程序启动时就自动创建实例呢,答案就是在成员变量中加入一个静态实例就可以

在类里面的静态实例主要起声明的作用,初始化静态实例需要在类外做

class Singleton {
public:static Singleton& GetInstance() {return instance;}void func() {cout << "func" << endl;}
private:Singleton() {}~Singleton() {}Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;static Singleton instance;
};Singleton Singleton::instance;

懒汉模式

懒汉模式只需要在调用接口时判断一下是否初始化过即可

class Singleton {
public:static Singleton* GetInstance() {if (instance == nullptr)instance = new Singleton();return instance;}void func() {cout << "func" << endl;}
private:Singleton() {}~Singleton() {}Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;static Singleton* instance;
};Singleton* Singleton::instance = nullptr;

需要注意的是,这里的线程是不安全的,需要设置互斥锁


http://www.mrgr.cn/news/16502.html

相关文章:

  • Java面试自我介绍
  • 每日一题——第六十八题
  • 指针空值类型-nullptr
  • 51单片机.之i2c读写eproom
  • 单门店共享自习室小程序系统源码搭建对接门禁和空开api
  • 深入理解并实现——归并排序【C语言】
  • ChatTTS 长音频合成和本地部署2种方式,让你的“儿童绘本”发声的实战教程(文末有福利)
  • 前胡基因组与伞形科香豆素的进化-文献精读42
  • 线性查找表的应用:用户登录注册程序
  • Scala之父Martin Odersky作序推荐的Scala速学版(第3版)出版
  • Java中的注解(Annotation)
  • ESM和CommonJS详解
  • vue3 element-plus 按下回车键搜索方法
  • 新160个crackme - 043-riijj_cm_20041121
  • jmeter响应断言、json断言、断言持续时间操作
  • mysql基础语法——个人笔记
  • Go 1.19.4 文件读写操作-Day 14
  • 100天带你精通Python——第8天面向对象编程
  • 【鬼灭之刃学英语 立志篇】2、义勇对炭治郎的怒斥
  • SAP LE学习笔记07 - MM与WM跨模块收货到仓库的流程中 如何实现 先上架再入库