muduo源码分析之Singleton
相关文件
源文件 \muduo\base\Singleton.h
测试文件 \muduo\base\tests\Singleton_test.cc
用途
单例模式用于保证一个类只能创建一个实例对象
使用
mudou中使用Singleton获取Test类的实例:
muduo::Singleton<Test>::instance()
知识点
- 使用pthread_once()保证单例且线程安全
- 使用::atexit()登记销毁函数,程序结束时自动析构
- 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类的对象自动析构
*/