muduo源码阅读(6):Singleton类

本文介绍了如何在C++中使用单例模式,通过pthread_once确保init函数仅执行一次,并结合atexit函数实现程序退出时的资源清理。还讨论了Singleton类构造析构函数的私有性和instance函数的静态特性。
摘要由CSDN通过智能技术生成

要点:

参考:https://blog.csdn.net/Leeds1993/article/details/52209392
(1)了解单例模式
实际应用中,有些对象,我们只需要一个就可以了,比如,一台计算机上可以连好几个打印机,但是这个计算机上的打印程序只能有一个,这里就可以通过单例模式来避免两个打印作业同时输出到打印机中,即在整个的打印过程中只有一个打印程序的实例。
单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中一个类只有一个实例。
(2)pthread_once

#include <pthread.h>

int pthread_once(pthread_once_t *once_control, void (*init_routine)(void));
pthread_once_t once_control = PTHREAD_ONCE_INIT;

本函数使用初值为PTHREAD_ONCE_INIT的once_control变量保证init_routine()函数在本进程执行序列中仅执行一次,且能保证线程安全。(我们还能用互斥锁的方式来实现线程安全,但效率没有pthread_once高)

(3)atexit

// atexit()函数用来注册程序正常终止时要被调用的函数
// 在一个程序中最多可以用atexit()注册32个处理函数
// 这些处理函数的调用顺序与其注册的顺序相反
// 即最先注册的最后调用,最后注册的最先调用

#include<stdlib.h>
int atexit(void (*func)(void));  // 登记的函数类型为不接受任何参数的void函数

(4)typedef char T_must_be_complete_type[sizeof(T) == 0 ? -1 : 1];
在C++中,类型有Complete type和Incomplete type之分,对于Complete type, 它的大小在编译时是可以确定的,而对于Incomplete type, 它的大小在编译时是不能确定的。 用delete删除一个只有声明但无定义的类型的指针(即不完整类型),是危险的。这通常导致无法调用析构函数(包括对象本身的析构函数、成员/基类的析构函数),从而泄露资源。 而通过 typedef char T_must_be_complete_type[sizeof(T) == 0 ? -1 : 1]; 这种做法能使当T为不完整类型时编译报错。(当T为不完整类型时,sizeof(T)给出的是0,根据代码规则,-1是不能作为数组的size的,因此,这里相当于强制编译器给出error而不是 warning)

代码Singleton.h

#ifndef MUDUO_BASE_SINGLETON_H
#define MUDUO_BASE_SINGLETON_H

#include <boost/noncopyable.hpp>
#include <pthread.h>
#include <stdlib.h> // atexit

namespace muduo
{

    template<typename T>
    class Singleton : boost::noncopyable
    {
    public:
    //instance函数是本类唯一暴露的接口,首先调用pthread_once执行init函数,
        //如果是第一次执行init,将为value_分配空间,否则不再执行,保证了唯一性,然后返还T类型的示例。
        static T& instance()
        {
            pthread_once(&ponce_, &Singleton::init);        //只执行init函数一次,init动态创建对象,即只有一个对象
            return *value_;
        }

    private:
        Singleton();
        ~Singleton();

        static void init()
        {
            value_ = new T();
            //它的作用是当程序正常终止时,调用指定的函数 func。可以在任何地方注册终止函数,
            //但它会在程序终止的时候被调用。这里所调用的终止函数是destroy。
            ::atexit(destroy);     
        }

        static void destroy()
        {
            /*
              定义了一个固定大小的char型数组,数组名为type_must_be_complete,数组大小是 是sizeof(T) == 0 ? -1 : 1
              若sizeof(T)非0,这个表达式的值为1,即typedef了一个大小为1的char型数组,否则定义一个大小为-1的数组。
              数组大小还能为负数?当然不能,于是就会报错,而且是编译期错误,于是就将一个动态运行时错误在编译时就发现了。

              接下来解释sizeof什么时候返回0. C/C++语言本身似乎没有这种情况,但有些编译器会作一些扩展,
              比如GCC对于incomplete type使用sizeof时,会返回0.那什么又叫做incomplete type呢,就是那些声明了,
              但没有定义的类型
            */
            typedef char T_must_be_complete_type[sizeof(T) == 0 ? -1 : 1];
            T_must_be_complete_type dummy; (void)dummy;
            /*
                C++标准允许通过一个 delete 表达式删除指向不完全类的指针。如果该类有一个非平凡的析构函数,
                或者有一个类相关的 delete 操作符,那么其行为就是无定义的。
                因此编译器作了这种扩展,以将这种未定义的行为转为编译期错误,帮助程序员们及早发现。


            */
            delete value_;
        }

    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;

}
#endif


instance函数是本类唯一暴露的接口,首先调用pthread_once执行init函数,如果是第一次执行init,将为value_分配空间,否则不再执行,保证了唯一性,然后返还T类型的示例。
为何Singleton类构造函数,析构函数是private的,且唯一对外接口instance是static 的?
C++构造函数/析构函数 设置成private的原因

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值