muduo源码分析之Singleton

相关文件

源文件 \muduo\base\Singleton.h
测试文件 \muduo\base\tests\Singleton_test.cc

用途

单例模式用于保证一个类只能创建一个实例对象

使用

mudou中使用Singleton获取Test类的实例:

muduo::Singleton<Test>::instance()

知识点

  1. 使用pthread_once()保证单例且线程安全
  2. 使用::atexit()登记销毁函数,程序结束时自动析构
  3. SFINAE,模板的使用技巧,判断一个类中是否有某函数

源代码

Singleton.h

// that can be found in the License file.
//
// Author: Shuo Chen (chenshuo at chenshuo dot com)

#ifndef MUDUO_BASE_SINGLETON_H
#define MUDUO_BASE_SINGLETON_H

#include "muduo/base/noncopyable.h"

#include <assert.h>
#include <pthread.h>
#include <stdlib.h> // atexit

namespace muduo
{

namespace detail
{

//Using SFINAE, we can detect wether a given class has a certain member function. 
// This doesn't detect inherited member functions!
// http://stackoverflow.com/questions/1966362/sfinae-to-check-for-inherited-member-functions
//用来判断是否有 no_destroy 函数
template<typename T>
struct has_no_destroy
{
  template <typename C> static char test(decltype(&C::no_destroy));
  template <typename C> static int32_t test(...);
  const static bool value = sizeof(test<T>(0)) == 1;
};
}  // namespace detail

template<typename T>
class Singleton : noncopyable   //不可拷贝
{
 public:
  Singleton() = delete;   //不可调用构造和析构函数
  ~Singleton() = delete;

  static T& instance()  //返回对象
  {
    //pthread_once能够保证init()该函数只调用一次
    //能够保证线程安全
    pthread_once(&ponce_, &Singleton::init); //第一次调用时创建对象,init()中创建
    assert(value_ != NULL);
    return *value_;
  }

 private:
  static void init()
  {
    value_ = new T();
    if (!detail::has_no_destroy<T>::value)
    {
      ::atexit(destroy);  //登记销毁函数,程序结束时自动调用destroy
    }
  }

  static void destroy()
  { 
    //什么是incomlete_type? sizeof T == 0
    //如class A; A *p; A是incomlete_type,仍然可以定义指针,编译时不报错,但A类型不完整
    //char T_must_be_complete_type[-1] 数组大小为-1时编译报错,能够在编译时发现错误
    typedef char T_must_be_complete_type[sizeof(T) == 0 ? -1 : 1];
    T_must_be_complete_type dummy; (void) dummy;

    delete value_;
    value_ = NULL;
  }

 private:
  static pthread_once_t ponce_;
  static T*             value_;
};

template<typename T>
pthread_once_t Singleton<T>::ponce_ = PTHREAD_ONCE_INIT;

template<typename T>
T* Singleton<T>::value_ = NULL;

}  // namespace muduo

#endif  // MUDUO_BASE_SINGLETON_H

测试示例

Singleton_test.cc
其中定义了两个类Test和TestNoDestroy
TestNoDestroy用于测试不注册Destroy时的内存泄露,其中定义了函数

void no_destroy();

在Singleton.h中

    if (!detail::has_no_destroy<T>::value)
    {
      ::atexit(destroy);  //登记销毁函数,程序结束时自动调用destroy
    }

如果类中含有no_destroy函数,则不登记销毁函数

#include "muduo/base/CurrentThread.h"
#include "muduo/base/Thread.h"

#include <stdio.h>

class Test : muduo::noncopyable
{
 public:
  Test()
  {
    printf("tid=%d, constructing %p\n", muduo::CurrentThread::tid(), this);
  }

  ~Test()
  {
    printf("tid=%d, destructing %p %s\n", muduo::CurrentThread::tid(), this, name_.c_str());
  }

  const muduo::string& name() const { return name_; }
  void setName(const muduo::string& n) { name_ = n; }

 private:
  muduo::string name_;
};

class TestNoDestroy : muduo::noncopyable
{
 public:
  // Tag member for Singleton<T>
  void no_destroy();

  TestNoDestroy()
  {
    printf("tid=%d, constructing TestNoDestroy %p\n", muduo::CurrentThread::tid(), this);
  }

  ~TestNoDestroy()
  {
    printf("tid=%d, destructing TestNoDestroy %p\n", muduo::CurrentThread::tid(), this);
  }
};

void threadFunc()
{
  //先打印,后改名
  printf("tid=%d, %p name=%s\n",
         muduo::CurrentThread::tid(),
         &muduo::Singleton<Test>::instance(),
         muduo::Singleton<Test>::instance().name().c_str());
  muduo::Singleton<Test>::instance().setName("only one, changed");
}

int main()
{
  muduo::Singleton<Test>::instance().setName("only one"); //获取对象,调用setName方法
  muduo::Thread t1(threadFunc); //创建线程,线程函数threadFunc
  t1.start();
  t1.join();
  //主线程打印
  printf("tid=%d, %p name=%s\n",
         muduo::CurrentThread::tid(),
         &muduo::Singleton<Test>::instance(),
         muduo::Singleton<Test>::instance().name().c_str());
  muduo::Singleton<TestNoDestroy>::instance();
  printf("with valgrind, you should see %zd-byte memory leak.\n", sizeof(TestNoDestroy));
}

/*
test output:

tid=6944, constructing 0x9474a10  
tid=6945, 0x9474a10 name=only one            //线程函数threadFunc打印
tid=6944, 0x9474a10 name=only one, changed   //主线程打印,名字被线程函数threadFunc修改
tid=6944, constructing TestNoDestroy 0x9474f10    //TestNoDestroy构建了,但没析构
with valgrind, you should see 1-byte memory leak.  //内存泄漏
tid=6944, destructing 0x9474a10 only one, changed   //Test类的对象自动析构

*/
posted @ 2021-05-10 23:17  零十  阅读(206)  评论(0编辑  收藏  举报