ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

单例以及模板类的静态成员变量的生命周期

2022-09-07 23:00:27  阅读:183  来源: 互联网

标签:生命周期 CGFunctionClass nullptr ptr flag static 单例 OnceSingle 模板


我们有如下的单例设计模式的实现:

template <typename T>
class OnceSingle {
public:
    OnceSingle() = delete;
    OnceSingle& operator=(const OnceSingle<T>& m) = delete;
    ~OnceSingle() = default;

    class CGFunctionClass {
        public:
            ~CGFunctionClass() {
                if (m_ptr != nullptr) {
                    delete m_ptr;
                    m_ptr = nullptr;
                }
            }
    };

    static T* getInstance() {
        std::call_once(s_flag, InitPtr);
        return m_ptr;
    }

private:
    static void InitPtr() {
        m_ptr = new T();
        static CGFunctionClass cg;
    }

private:
    static T* m_ptr;
    static std::once_flag s_flag;
};

template<typename T>
T* OnceSingle<T>::m_ptr = nullptr;

template<typename T>
std::once_flag OnceSingle<T>::s_flag;

有如下的单元测试函数:

TEST(MyUtil, test_singleton) {
    int *raw_ptr = OnceSingle<int>::getInstance();
    int *same_raw_ptr = OnceSingle<int>::getInstance();
    EXPECT_EQ(raw_ptr, same_raw_ptr);
    raw_ptr = nullptr;
    same_raw_ptr = nullptr;
}

在这个模板单例类中,存在一个裸指针 m_ptr,当我们的单元测试结束的时候,静态对象 static CGFunctionClass cg会进行析构,从而保证了这个裸指针所指向的对象也能够得到析构,从而避免了内存泄漏。

注意到这个静态CGFunctionClass的对象cg,是与 new T 同时出现的。

那么我们能不能将这个cg对象定义为这个类的静态成员变量呢?这样能够让代码的结构更加清晰。同时也让这个静态变量在析构的时候能够析构这个裸指针。

答案是否定的!

现在我们进行分析,我们有如下代码


template <typename T>
class OnceSingle {
public:
    OnceSingle() = delete;
    OnceSingle& operator=(const OnceSingle<T>& m) = delete;

    ~OnceSingle() = default;
    class CGFunctionClass {
        public:
            ~CGFunctionClass() {
                if (m_ptr != nullptr) {
                    delete m_ptr;
                    m_ptr = nullptr;
                }
            }
    };

    static T* getInstance() {
        std::call_once(s_flag, InitPtr);
        return m_ptr;
    }
private:
    static void InitPtr() {
        // s;
        // cg;
        m_ptr = new T();
    }

private:
    static T* m_ptr;
    static std::once_flag s_flag;
    static CGFunctionClass cg;
    static MyString s;
};

template<typename T>
T* OnceSingle<T>::m_ptr = nullptr;

template<typename T>
std::once_flag OnceSingle<T>::s_flag;

template<typename T>
typename OnceSingle<T>::CGFunctionClass OnceSingle<T>::cg;

template<typename T>
MyString OnceSingle<T>::s("Class Static Variable");

我们对 MyString 这个类型的构造函数和析构函数增加了字符串输出函数,使其在构造和析构的时候能够打印出信息。我们继续运行之前的单元测试,发现这个 s 对象的并没有被构造,更不用说析构了,由此可知 cg 这个变量同时也没有构造和析构,因此上述代码的执行会造成内存泄漏的问题。
我们把 InitPtr 函数中注释掉的两行取消注释,发现 s 对象成功构造了。
查阅《C++ Primer》 p588 有相关描述
image

C++中模板类的静态成员变量,只有在其使用时,才会进行构造
我们把上述代码中 InitPtr 的两行取消注释, 我们发现, 在程序的一开始就会输出 "Class Static Variable", 说明这个静态变量构造成功了。

如此一来,还是要在 InitPtr 函数中增加与单例对象无关的代码,为了避免发生内存泄漏,仍采用第一种写法。

标签:生命周期,CGFunctionClass,nullptr,ptr,flag,static,单例,OnceSingle,模板
来源: https://www.cnblogs.com/qwerty-ll/p/16667449.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有