从零开始学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 条评论
登录 后参与评论

相关文章

来自专栏Java Edge

Elastic Stack从入门到实践

37810
来自专栏Java成神之路

PL/SQL学习笔记_02_游标

        在 PL/SQL 程序中,对于处理多行记录的事务经常使用游标来实现。 

944
来自专栏xingoo, 一个梦想做发明家的程序员

基于ELK的数据分析实践——满满的干货送给你

很多人刚刚接触ELK都不知道如何使用它们来做分析,经常会碰到下面的问题: 安装完ELK不知从哪下手 拿到数据样本不知道怎么分解数据 导入到elastics...

3407
来自专栏灯塔大数据

Python-解决Cx_Oracle查询时UnicodeDecodeError的问题

近期在项目中,要对1张100多万条记录的表进行查询,然后进行一些数据的统计,但是在这个过程中,发现只查询出来几条数据就出现了UnicodeDecodeErro...

2826
来自专栏玩转JavaEE

elasticsearch API约定(一)

前面我们介绍了一些REST API的基本用法,使读者对API的使用有了一个直观的了解,本文我们来介绍下Elasticsearch中的API约定,这些约定适用于大...

943
来自专栏维C果糖

史上最简单的 MySQL 教程(四)「SQL 基本操作 之 库操作」

根据操作对象的不同,咱们可以将 SQL 的基本操作分为三类,分别为:库操作、表(字段)操作和数据操作。

42713
来自专栏别先生

Oracle的登陆问题和初级学习增删改查(省略安装和卸载)

1:学习Oracle首先需要安装Oracle,网上已经有很多很多教程了,这里不做叙述,自己百度即可,这里安装的标准版,个人根据需求安装学习或者企业开发即可。如果...

1786
来自专栏青玉伏案

Swift3.0服务端开发(五) 记事本的开发(iOS端+服务端)

前边以及陆陆续续的介绍了使用Swift3.0开发的服务端应用程序的Perfect框架。本篇博客就做一个阶段性的总结,做一个完整的实例,其实这个实例在《Swift...

2087
来自专栏性能与架构

分库后如何处理分页?

在数据量过大以后,通常都会进行分库操作,把一张表拆分到不同数据库中 例如 tb1 表被拆分到3个库中,分库1、分库2、分库3 现在想执行分页操作 SELECT ...

2928
来自专栏鸿的学习笔记

读会搜索引擎

搜索引擎一般由索引管理器,索引检索器,索引构建器,文档管理器组成。 索引管理器,顾名思义是管理带有索引结构的数据,负责对索引的访问。那么索引是怎么管理的呢?不...

1441

扫码关注云+社区