谈谈C++中的单例

写C++的时候用到单例,于是很自然的写出如下的代码:

namespace tlanyan {
  class Foo {
  private:
    static Foo* _instance;
    Foo() {}
    // other members

  public:
    static Foo* getInstance() {
      if (_instance == NULL) {
        _instance = new Foo();
      }
      return _instance;
    }
    ~Foo() {
      // clean codes
    }
    // other members and codes
  };
  Foo* Foo::_instance = NULL;
}

代码的本意:静态成员函数getInstance获取单例指针,并且在析构函数中做一些收尾工作。

运行代码后发现析构函数死活不执行,难道一个单例模式都能写错?反复确认,没发现问题所在,于是上万能的StackOverflow上找原因。正好有伙计有同样的疑惑,有哥们给出了一个可行的方案。根据其答案修改代码如下:

namespace tlanyan {
  class Foo {
  private:
    Foo() {}
    // other members

  public:
    static Foo& getInstance() {
      static Foo _instance;
      return _instance;
    }
    ~Foo() {
      // clean codes
    }
    // other members and codes
  };
}

对比前一段代码,主要改动是移除了静态指针成员,改用函数内的静态成员。由于_instance是函数内的静态成员,在首次调用时被初始化(感谢无参构造函数),之后调用将略过初始化而执行后续代码;函数返回实例的引用,故而每次调用得到的是同一个对象,达到了单例的目的;程序执行结束后,实例的析构函数被自动调用,析构函数中的代码正确执行。

问题解决了,但什么原因造成第一段单例代码的析构函数不执行呢?

这是由于C++持有对象的方式造成的(或者说C++允许程序员手动控制内存引起)。Java/PHP等带有回收机制的语言,持有对象的方式是通过指针,程序员申请对象后会自动分配内存,系统负责跟踪和回收无用的对象和存在。C++允许开发人员以变量的方式持有对象,例如:Foo foo [= Foo(args)]。变量初始化后获得对象的引用,离开作用域后,系统销毁执行栈,对象自动被析构。C++也可以以指针的形式获得对象的引用: Foo* foo = new Foo(args)。这种方式分配的对象,需要开发人员手动管理。如果不执行delete,对象和分配的内存将一直存在,直到程序退出后,才由操作系统回收。如下代码可说明这点:

namespace tlanyan {
  void foo() {
    Foo foo;  // 声明变量,编译器会自动初始化变量
    Foo* ptr = new Foo();  // 声明对象指针,同时为对象开辟内存
    // awesome codes
    return;    // 离开作用域, foo对象将被自动析构;如果之前没有调用过delete ptr, ptr指向的对象将一直存在;如果delete ptr,析构函数将被执行。也可以将ptr指向的对象赋值给外层作用域的指针,此时有多个指针指向同一个变量
  }
}

从上面的代码可以看出,对象没有引用计数的情况下,编译器和系统不敢随便回收new出来的对象内存:多个指针指向同一个对象,delete了对象可能会导致其他代码崩溃;释放内存后,其他指针再delete会多次delete同一块内存,引发不可预知风险。

综上,指针单例析构函数没有被调用的原因是: 自己new的对象,需要自己delete,别指望别人帮你正确调用析构函数。

问题的解决方案有以下几种:

  1. 同StackOverflow上回答,改用变量方式持有单例对象。程序运行结束前会销毁所有变量,变量的析构函数将被正确调用;
  2. 在main函数退出前delete单例。例如增加一个destroy的静态成员函数,将指针指向的对象销毁;
  3. 使用auto_ptr/unique_ptr等智能指针。

如有其它解决方案,欢迎交流指正!

以上就是谈谈C++中的单例的详细内容,更多关于c++ 单例的资料请关注我们其它相关文章!

(0)

相关推荐

  • 老生常谈C++的单例模式与线程安全单例模式(懒汉/饿汉)

    1 教科书里的单例模式 我们都很清楚一个简单的单例模式该怎样去实现:构造函数声明为private或protect防止被外部函数实例化,内部保存一个private static的类指针保存唯一的实例,实例的动作由一个public的类方法代劳,该方法也返回单例类唯一的实例. 上代码: class singleton { protected: singleton(){} private: static singleton* p; public: static singleton* instance()

  • C++ 单例模式的几种实现方式研究

    单例模式 单例模式,可以说设计模式中最常应用的一种模式了,据说也是面试官最喜欢的题目.但是如果没有学过设计模式的人,可能不会想到要去应用单例模式,面对单例模式适用的情况,可能会优先考虑使用全局或者静态变量的方式,这样比较简单,也是没学过设计模式的人所能想到的最简单的方式了. 一般情况下,我们建立的一些类是属于工具性质的,基本不用存储太多的跟自身有关的数据,在这种情况下,每次都去new一个对象,即增加了开销,也使得代码更加臃肿.其实,我们只需要一个实例对象就可以.如果采用全局或者静态变量的方式,会

  • C++实现 单例模式实例详解

    设计模式之单例模式C++实现 一.经典实现(非线程安全) class Singleton { public: static Singleton* getInstance(); protected: Singleton(){} private: static Singleton *p; }; Singleton* Singleton::p = NULL; Singleton* Singleton::getInstance() { if (NULL == p) p = new Singleton()

  • c++中的单例类模板的实现方法详解

     1.什么是单例模式 在架构设计时,某些类在整个系统生命周期中最多只能有一个对象存在 ( Single Instance ).如超市收银系统,其外观主要由显示器(1个).扫描枪(1个).收款箱(1个)组成,在系统正常运行期间这三部分都是唯一存在的:也就是说,显示器.扫描枪.收款箱这三部分都应该有各自的类,并且每个类只能唯一地实例化一个对象,基于这种模式的程序设计,称为单例模式. !!!单例模式只能创建一个对象,且该对象的生命周期伴随系统的整个运行期间. 2.怎么实现单例模式 思考:如何定义一个类

  • 详解C++实现线程安全的单例模式

    在某些应用环境下面,一个类只允许有一个实例,这就是著名的单例模式.单例模式分为懒汉模式,跟饿汉模式两种. 首先给出饿汉模式的实现 正解: template <class T> class singleton { protected: singleton(){}; private: singleton(const singleton&){};//禁止拷贝 singleton& operator=(const singleton&){};//禁止赋值 static T* m

  • C++和java设计模式之单例模式

    单例模式(Singleton),保证一个类仅有一个实例,并提供一个访问它的全局访问点.其构造过程由自身完成,可以将构造方法定义为private型的,这样外界就只能通过定义的静态的函数Instance()构造实例,这个函数的目的就是返回一个类的实例,在此方法中去做是否有实例化的判断.客户端不再考虑是否需要去实例化的问题,把这些都交给了单例类自身.通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象.一个最好的办法,就是让类自身负责保存它的唯一实例.这个类可以保证没有其他实例可

  • C++单例模式应用实例

    本文实例讲述了C++单例模式及其相关应用方法,分享给大家供大家参考.具体方法分析如下: 定义: 一个类有且仅有一个实例,并且提供一个访问它的全局访问点. 要点: 1.类只能有一个实例: 2.必须自行创建此实例: 3.必须自行向整个系统提供此实例. 实现一:单例模式结构代码 singleton.h文件代码如下: #ifndef _SINGLETON_H_ #define _SINGLETON_H_ class Singleton { public: static Singleton* GetIns

  • 使用设计模式中的单例模式来实现C++的boost库

    线程安全的单例模式 一.懒汉模式:即第一次调用该类实例的时候才产生一个新的该类实例,并在以后仅返回此实例. 需要用锁,来保证其线程安全性:原因:多个线程可能进入判断是否已经存在实例的if语句,从而non thread safety. 使用double-check来保证thread safety.但是如果处理大量数据时,该锁才成为严重的性能瓶颈. 1.静态成员实例的懒汉模式: class Singleton { private: static Singleton* m_instance; Sing

  • C++单例模式的实例详解

    单例模式概述 个人认为单例模式是设计模式中最为简单.最为常见.最容易实现,也是最应该熟悉和掌握的模式.且不说公司企业在招聘的时候为了考察员工对设计的了解和把握,考的最多的就是单例模式. 单例模式解决问题十分常见,我们怎样去创建一个唯一的变量(对象)?在基于对象的设计中我们可以通过创建一个全局变量(对象)来实现,在面向对象和面向过程结合的设计范式(如 C++中)中,我们也还是可以通过一个全局变量实现这一点.但是当我们遇到了纯粹的面向对象范式中,这一点可能就只能是通过单例模式来实现了,可能这也正是很

  • C++单例类模板详解

    单例类 描述 指在整个系统生命期中,一个类最多只能有一个实例(instance)存在,使得该实例的唯一性(实例是指一个对象指针)  , 比如:统计在线人数 在单例类里,又分为了懒汉式和饿汉式,它们的区别在于创建实例的时间不同: 懒汉式 : 指代码运行后,实例并不存在,只有当需要时,才去创建实例(适用于单线程) 饿汉式 : 指代码一运行,实例已经存在,当时需要时,直接去调用即可(适用于多线程) 用法 将构造函数的访问属性设置为private, 提供一个GetInstance()静态成员函数,只能供

  • 从C++单例模式到线程安全详解

    先看一个最简单的教科书式单例模式: class CSingleton { public: static CSingleton* getInstance() { if (NULL == ps) {//tag1 ps = new CSingleton; } return ps; } private: CSingleton(){} CSingleton & operator=(const CSingleton &s); static CSingleton* ps; }; CSingleton*

随机推荐