从零开始学C++之模板(四):用模板实现单例模式(线程安全)、模板方式实现动态创建对象

一、用模板实现单例模式

前面的文章中,用过多种方法实现单例模式,现在用模板方式来实现:

为了实现线程安全,需要在linux 下使用pthread_mutex_t 加锁,请使用g++ 编译并需要链接 -lpthread

使用的是double-check lock, 指针instance_ 最好声明为volatile,防止优化。

Singleton.h:

/*************************************************************************
    > File Name: Singleton.h
    > Author: Simba
    > Mail: dameng34@163.com
    > Created Time: Wed 02 Apr 2014 01:33:39 AM PDT
 ************************************************************************/

#ifndef _SINGLETON_H_
#define _SINGLETON_H_

#include <iostream>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

using namespace std;

template < typename T > class Singleton
{

public:
    static T &GetInstance()
    {
        Init();
        return *instance_;
    }

private:
    static void Init()
    {
        if (instance_ == 0)
        {

            pthread_mutex_lock(&g_mutex);
            if (instance_ == 0)
            {
                instance_ = new T;
                atexit(Destroy);    //程序结束时调用注册的函数
            }
            pthread_mutex_unlock(&g_mutex);
        }
    }

    static void Destroy()
    {
        delete instance_;
    }

    Singleton(const Singleton &other);
    Singleton &operator=(const Singleton &other);
    Singleton();
    ~Singleton();

    static T * volatile instance_;
    static pthread_mutex_t g_mutex;
};

template < typename T >
T * volatile Singleton < T >::instance_ = 0;

template < typename T >
pthread_mutex_t Singleton<T> ::g_mutex = PTHREAD_MUTEX_INITIALIZER;

#endif              // _SINGLETON_H_

main.cpp:

/*************************************************************************
    > File Name: main.cpp
    > Author: Simba
    > Mail: dameng34@163.com
    > Created Time: Wed 02 Apr 2014 01:30:13 AM PDT
 ************************************************************************/

#include "Singleton.h"

class ApplicationImpl
{

public:
    ApplicationImpl()
    {
        cout << "ApplicationImpl ..." << endl;
    }

    ~ApplicationImpl()
    {
        cout << "~ApplicationImpl ..." << endl;
    }

    void Run()
    {
        cout << "Run ..." << endl;
    }
};

typedef Singleton < ApplicationImpl > Application;

void *routine(void *arg)
{
    Application::GetInstance().Run();
}

int main(void)
{
    Application::GetInstance().Run();

    pthread_t tid;
    int ret;
    if ((ret = pthread_create(&tid, NULL, routine, NULL)) != 0)
    {
        fprintf(stderr, "pthread create: %s\n", strerror(ret));
        exit(EXIT_FAILURE);
    }

    Application::GetInstance().Run();

    pthread_join(tid, NULL);

    return 0;
}

即 将Singleton 实现为模板类,将ApplicationImpl 类包装成单例模式类,可以看到构造函数和析构函数都只调用了一次。程序使用一个小技巧,用axexit 函数注册了程序结束时需要调用的函数。

二、模板方式实现动态创建对象

前面的文章曾经使用宏定义的方式实现动态创建对象,现在在 DynBase.h 中用模板类将宏定义替换掉,其他代码不变:

//class Register
//{
//public:
//    Register(const string &name, CREATE_FUNC func)
//    {
//        DynObjectFactory::Register(name, func);
//    }
//};
//
//#define REGISTER_CLASS(class_name) \
//class class_name##Register { \
//public: \
//    static void* NewInstance() \
//    { \
//        return new class_name; \
//    } \
//private: \
//    static Register reg_; \
//}; \
//Register class_name##Register::reg_(#class_name, class_name##Register::NewInstance)

template <typename T>
class DelegatingClass
{
public:
    DelegatingClass(const string &name)
    {
        DynObjectFactory::Register(name, &(DelegatingClass::NewInstance));
    }

    static void *NewInstance()
    {
        return new T;
    }
};

#define REGISTER_CLASS(class_name) DelegatingClass<class_name> class##class_name(#class_name)

即 REGISTER_CLASS(class_name) 宏定义展开会构造一个模板类实例对象,调用3次宏定义即3个模板类实例对象,调用构造函数

DelegatingClass(const string &name),进而调用Register 完成注册,接下去的流程与以前的程序一样,不再赘述。输出如下:

参考:

C++ primer 第四版 Effective C++ 3rd C++编程规范

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Crossin的编程教室

Python单例模式(Singleton)的N种实现

很多初学者喜欢用全局变量,因为这比函数的参数传来传去更容易让人理解。确实在很多场景下用全局变量很方便。不过如果代码规模增大,并且有多个文件的时候,全局变量就会变...

902
来自专栏GopherCoder

『Go 语言学习专栏』-- 第五期

1496
来自专栏Golang语言社区

GO语言标准库概览

在Go语言五周系列教程的最后一部分中,我们将带领大家一起来浏览一下Go语言丰富的标准库。 Go标准库包含了大量包,提供了丰富广泛的功能特性。这里提供了概览仅仅是...

2544
来自专栏微信公众号:Java团长

Hi,我们再来聊一聊Java的单例吧

单例(Singleton)应该是开发者们最熟悉的设计模式了,并且好像也是最容易实现的——基本上每个开发者都能够随手写出——但是,真的是这样吗? 作为一个Java...

581
来自专栏西枫里博客

Ajax处理success回调函数返回的json数据。

站长最近在项目中用调用一个分类的子数据,由于表单要填写的数据较多,为了实现无刷新的选择操作,就使用ajax做了异步查询。查询的结果因为是多条数据,一直以来动用a...

612
来自专栏码匠的流水账

聊聊flink的SourceFunction

flink-streaming-java_2.11-1.6.2-sources.jar!/org/apache/flink/streaming/api/func...

733
来自专栏菩提树下的杨过

Linq2Sql数据实体外部更新时“不能添加其键已在使用中的实体”的解决办法

Linq to Sql中,如果我们想在DataContext外部修改一个实体的值,然后把引用传入到DataContext中,再利用Attach附加后更新,代码如...

2025
来自专栏谦谦君子修罗刀

程序员面试闪充 -- KVC&KVO

一、键值编码KVC kvc&kvo视频讲解 1、介绍 由于oc的语言特性,使得开发者根本不必进行任何操作就可以进行属性的动态读写,这种方式就是Key Valu...

25811
来自专栏博客园

设计模式学习-单例模式

但是这么做不感觉有问题吗?假如这个类我们并不使用或在程序启动很久以后我们才使用,那么这个对象的预创建不就很浪费吗?并且如果这个对象的创建需要很大的资源,那......

573
来自专栏小狼的世界

JavaScript设计模式学习(四)单件(Singleton Pattern)

单件是JavaScript中最基本、最有用的设计模式,而你今后也会经常的使用这个模式。通过单件,我们可以把统一到一个逻辑单元中并且提供一个唯一的入口,这就保证你...

1024

扫码关注云+社区