模板静态成员变量需要使用才会构造

简介

今天在写单例模式时,计划使用隐藏类的析构来释放内存,之前一直采用的注册方式(atexit)。但是在使用valgrind检测内存泄露时发现出现了error,之前使用注册方式从未出现。于是在隐藏类的析构加了printf,发现未输出,于是在构造又加了printf,发现也未输出。可想这个类没有发生构造,但是相比于其他静态变量,其他静态变量都是正常的,唯一的不同点在于这个隐藏类变量从未使用过。

代码
#ifndef __SINGLETON_H__
#define __SINGLETON_H__

#include "mutex.h"
#include "exception.h"
#include "refcount.h"
#include <bits/move.h>

namespace eular {

template<typename T>
class Singleton {
public:
    struct SObject
    {
    private:
        friend class Singleton<T>;
        SObject(T *obj, RefCount *ref) :
            mObj(obj),
            mRefPtr(ref)
        {
            assert(mRefPtr != nullptr);
            mRefPtr->ref();
        }

    public:
        SObject(const SObject &other)
        {
            mObj = other.mObj;
            mRefPtr = other.mRefPtr;
            mRefPtr->ref();
        }

        SObject &operator=(const SObject &other)
        {
            mObj = other.mObj;
            mRefPtr = other.mRefPtr;
            mRefPtr->ref();
            return *this;
        }

        ~SObject()
        {
            mObj = nullptr;
            mRefPtr->deref();
            mRefPtr = nullptr;
        }

        T *operator->()
        {
            if (mObj == nullptr) {
                throw Exception("nullptr object");
            }
            return mObj;
        }

        T &operator*()
        {
            if (mObj == nullptr) {
                throw Exception("nullptr object");
            }
            return *mObj;
        }

        operator T*()
        {
            return mObj;
        }

        operator const T*() const
        {
            return mObj;
        }

    private:
        T *mObj;
        RefCount *mRefPtr;
    };

    template<typename... Args>
    static SObject get(Args... args)
    {
        // 编译期间检测类型完整性
        static_assert(sizeof(T), "incomplete type");
        AutoLock<Mutex> lock(mMutex);
        if (mInstance == nullptr) {
            mInstance = new T(std::forward<Args>(args)...);
            (void)mDeleter; // 模板静态成员变量需要使用才会构造
            // ::atexit(Singleton<T>::free); // 在mian结束后调用free函数
        }
        SObject obj(mInstance, &mRef);
        return obj;
    }

    /**
     * @brief 重置实例, 会返回一个新的地址,所以原来的会失效,对于单例模式,此方法用的不太多 
     */
    template<typename... Args>
    static SObject reset(Args... args)
    {
        AutoLock<Mutex> lock(mMutex);
        if (mRef.load() == 0) {
            if (mInstance != nullptr) {
                delete mInstance;
                mInstance = nullptr;
            }
            mInstance = new T(std::forward<Args>(args)...);
        }

        SObject obj(mInstance, &mRef);
        return obj;
    }

    static void free()
    {
        if (mRef.load() > 0) {
            return;
        }

        AutoLock<Mutex> lock(mMutex);
        if (mInstance != nullptr) {
            delete mInstance;
            mInstance = nullptr;
        }
    }

private:
    class Deleter {
    public:
        ~Deleter()
        {
            Singleton<T>::free();
        }
    };

private:
    static T *mInstance;
    static RefCount mRef;
    static Mutex mMutex;
    static Deleter mDeleter;

    Singleton() {}
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
};

template<typename T>
T *Singleton<T>::mInstance = nullptr;

template<typename T>
Mutex Singleton<T>::mMutex;

template<typename T>
RefCount Singleton<T>::mRef;

template<typename T>
typename Singleton<T>::Deleter Singleton<T>::mDeleter;

} // namespace eular

#endif  // __SINGLETON_H__
测试代码
#include <stdio.h>

template <class T>
class StaticObject {
public:
    class None {
    public:
        None() { printf("%s()\n", __func__); }
        ~None() { printf("%s()\n", __func__); }
    };

    static None none;
};

template<class T>
typename StaticObject<T>::None StaticObject<T>::none;

// static StaticObject<int>::None none; // 1

int main(int argc, char **argv)
{
    StaticObject<int> a;
    // a.none; // 2
    return 0;
}

gcc version 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04)
g++ main.cc -o main -std=c++11 -g -O0

放开1处代码,注释2处代码注释1,放开2均可正常看到输出
同时注释1、2处代码无法看到输出
同时放开两处代码则会输出两次
由此可见,模板类的静态成员变量需要使用才会构造

GDB

下面是放开a.none的调试
请添加图片描述

静态成员变量的初始化在__libc_start_main之后,main之前。由此也可以确定,静态成员变量的创建发生在运行期间
后话

将同样的测试代码放在vs2019中同样会不输出,所以这应该是标准行为。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值