C++设计模式:单例模式

单例模式的使用相当广泛,当一个类只允许被构造一个对象的时候尤为常见

禁止外面直接调用构造器

禁止被拷贝和移动

// 从设计上禁止行为
MyClass a;
MyClass *p = new MyClass;
MyClass b(a);
MyClass c(std::move(a));

c++中如果不想因为调用某个函数而产生某种效果,那么你就不应该声明这个函数,但是这个存在5个例外,五大函数如果你不定义它,编译器会默认生成空函数体的五大函数,已完成C++自己的内存管理机制。

  MyClass() {}
  MyClass(const MyClass &) = delete;
  MyClass(MyClass &&) = delete;
  void operator=(const MyClass &) = delete;
  void operator=(MyClass &&) = delete;

单例的一般调用方式为:

#include "myclass.hpp"

int main() {
	MyClass::getInstance().print();
    return 0;
}

单例模式分为饿汉型和懒汉型

饿汉型:

在程序一起来的时候就加载到.data区,绝对线程安全

对象型:
// myclass.hpp
class MyClass {
 public:
  ~MyClass() {}
  static MyClass& getInstance() { return me_; }

  void print() { ::printf("hello\n"); }

 private:
  MyClass() { ::printf("new\n"); }
  MyClass(const MyClass&) = delete;
  MyClass(MyClass&&) = delete;
  void operator=(const MyClass&) = delete;
  void operator=(MyClass&&) = delete;

 private:
  static MyClass me_;
};
// 假如在myclass.cpp文件中定义,定义有且仅有一次,不管你在哪,只要能最终被链接器成功link
#include "myclass.hpp"

MyClass MyClass::me_{};
指针型:
class MyClass {
 public:
  ~MyClass() {}
  static MyClass& getInstance() { return *me_; }

  void print() { ::printf("hello\n"); }

 private:
  MyClass() { ::printf("new\n"); }
  MyClass(const MyClass&) = delete;
  MyClass(MyClass&&) = delete;
  void operator=(const MyClass&) = delete;
  void operator=(MyClass&&) = delete;

 private:
  static MyClass *me_;
};

// 假如在myclass.cpp文件中定义,定义有且仅有一次,不管你在哪,只要能最终被链接器成功link
#include "myclass.hpp"

MyClass *MyClass::me_{ new MyClass };  // 同种类型互为友元,可访问自己的私有属性和操作,构造指针对象时构造实际对象
懒汉型:

在函数getInstance()被调用的时候就加载到.data区,线程安全需要讨论

对象型:

线程安全,static对象只能创建一次,底层包含call_once机制

// myclass.hpp
class MyClass {
 public:
  ~MyClass() {}
  static MyClass& getInstance() {
      static MyClass me_;
      return me_; 
  }

  void print() { ::printf("hello\n"); }

 private:
  MyClass() { ::printf("new\n"); }
  MyClass(const MyClass&) = delete;
  MyClass(MyClass&&) = delete;
  void operator=(const MyClass&) = delete;
  void operator=(MyClass&&) = delete;
};
指针型:
class MyClass {
 public:
  ~MyClass() {}
  static MyClass& getInstance() {
    if (nullptr == me_) {  // [1]
      me_ = new MyClass;   // [2]
    }
    return *me_;  // [3]
  }

  void print() { ::printf("hello\n"); }

 private:
  MyClass() { ::printf("new\n"); }
  MyClass(const MyClass&) = delete;
  MyClass(MyClass&&) = delete;
  void operator=(const MyClass&) = delete;
  void operator=(MyClass&&) = delete;

 private:
  static MyClass* me_;
};
// 假如在myclass.cpp文件中定义,定义有且仅有一次,不管你在哪,只要能最终被链接器成功link
#include "myclass.hpp"

MyClass *MyClass::me_{ nullptr };  // 构造指针对象时,并未产生实际对象

但多线程调用时,线程1执行[1]时2发现me_是null,正打算执行[2]时,时间片流转到了线程2,线程2也发现me_是null,在他的时间里做了new的操作后,又轮到了线程1去执行[2],此时就创建了两个对象,造成内存泄露

修改方法:

class MyClass {
 public:
  ~MyClass() {}
  static MyClass& getInstance() {
    std::call_once(once_, [&]{ me_ = new MyClass; });
    return *me_;
  }

  void print() { ::printf("hello\n"); }

 private:
  MyClass() { ::printf("new\n"); }
  MyClass(const MyClass&) = delete;
  MyClass(MyClass&&) = delete;
  void operator=(const MyClass&) = delete;
  void operator=(MyClass&&) = delete;

 private:
  static std::once_flag once_; 
  static MyClass* me_;
};

tip: 普通的双重判断可能存在CPU执行的指令和我们现象顺序不同的情况,需要人为设置内存操作顺序,这里不讨论那种解法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

歪锅锅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值