计算机系统应用教程网站

网站首页 > 技术文章 正文

C++使用Poco 超简单的实现单例模式

btikc 2024-09-24 08:07:55 技术文章 17 ℃ 0 评论

POCO C++是一个开源的C++类库的集合,它主要提供简单的、快速的网络和可移植应用程序的C++开发,这个类库和C++标准库可以很好的集成并填补C++标准库的功能空缺。POCO库的模块化、高效的设计及实现使得POCO特别适合嵌入式开发。在嵌入式开发领域,由于C++既适合底层(设备I/O、中断处理等)和高层面向对象开发,越来越流行。当然POCO也准备好了面对企业级挑战。

Poco C++库是:

  • 一系列C++类库,类似Java类库,.Net框架,Apple的Cocoa;
  • 侧重于互联网时代的网络应用程序
  • 使用高效的,现代的标准ANSI/ISO C++,并基于STL
  • 高可移值性,并可在多个平台下可用
  • 开源,并使用Boost Software License发布
  • 不管是否商用,都完全免费

天使用Poco提供的单例模板来完成单例的使用,单例的目的是为实现结构对象中只有一个实例,一般使用C++ 写法有下面几种写法

1. 懒汉式

//MyClass.h

class MyClass
{
public:
    static MyClass* getInstance()
    {
        if (m_instance == nullptr) 
        {
          m_instance = new MyClass();
        }
    return m_instance;
}
private:
    MyClass() {} //私有构造函数
    MyClass(const MyClass& my);//禁止拷贝构造函数
    MyClass& operator=(const MyClass &my); //禁止赋值操作
    static MyClass* m_instance;
}

//MyClass.cpp
MyClass* MyClass::m_instance = nullptr;// 存放在Cpp中,若放在.h中,.h被包含多次时重复定义

上面例子存在的问题是:

线程不安全

不能灵活析构单例

解决方案: 在getInstance()中使用m_instance时加锁

class MyClass
{
private:
    std::unique_lock<std::mutex> locker(m_mutex);
public:
    static MyClass* getInstance()
    {
    std::unique_lock<std::mutex> locker(m_mutex);
    if (m_instance == nullptr) 
    {
        m_instance = new MyClass();
    }
    return m_instance;
}

    static void destory()
    {
        std::unique_lock<std::mutex> locker(m_mutex);
        if (m_instalce) 
        {
    	    delete m_instance;
           m_instance = nullptr;
        }
    }
}

以上代码解决了线程安全和单例内存释放的问题,但是如果单例对象被多次访问需要多次加锁,效率较低,可以使用双检查机制

static MyClass* getInstance()
{
    if (m_instance == nullptr) 
    {
        std::unique_lock<std::mutex> locker(m_mutex);
        if (m_instance == nullptr)
        {
            m_instance = new MyClass();
        }
    }
    return m_instance;
}

这样处理的原因: m_instance != nullptr时表示一定被new过了; m_instance == null,表示可能被new 过,也可能没有被new过。

2. 饿汉式

饿汉式单例代码如下,线程安全的,但是不能手动灵活释放对象

static MyClass* getInstance()
{
    static MyClass c;
    return &c;
}

3. std::call_once

std::call_once 是C++11新增功能,能够保证在多线程情况下指定函数只被调用一次;具备互斥量的能力,且比互斥量消耗的资源更少;需要与一个标记结合使用std::once_flag。

class MyClass
{
  public:
      static MyClass* getInstance()
      {
        std::call_once(flag, [&]()
        {
          m_instace = new MyClass;
        }
        return m_instance;
  }

  static void destory()
  {
    if (m_instance) 
    {
        delete m_instance;
        m_instance = nullptr;
    }
    }
private:
    static MyClass* m_instance;
    static std::once_flag flag;
}

MyClass* MyClass::m_instance = nullptr; //定义在.cpp中
std::once_flag MyClass::flag;

缺点是:不能实现多次创建、销毁单例, 调用 getInstance()、destory(),成功创建和销毁一次单例,再次调用 getInstance()、destory() 失败。


以上方式比较老土,个人还是最喜欢用Poco的SingletonHolder模板来实现,现在我们一起来看Poco的终极大招,结合Visual Stduio 2022 通过CMake来写个完整的单例,还没在Visual Stduio中使用CMake,正好练下手。

假设我想把我的Ck600这个类变成单例,怎么做了,直接上代码

Ck600.h

#ifndef CK600_H
#define CK600_H

#include <Poco/Logger.h>
using Poco::Logger;

class Ck600
{
public:
	static Ck600* getInstance();
protected:
	static Poco::Logger& LOGGER;
};

#endif

看到没只需要在代码中提供一个获取实例的方法getInstance即可

Ck600.cpp

#include <Poco/SingletonHolder.h>
#include "Ck600.h"

Poco::Logger& Ck600::LOGGER = Poco::Logger::get("Ck600");
static Poco::SingletonHolder<Ck600> holder;
Ck600* Ck600::getInstance()
{
	return holder.get();
}

只需要我用包含头文件Poco/SingletonHolder.h,然后使用holder包装下即可

下面我们看下执行结果

test.cpp

#include <iostream>
#include "Ck600.h"

using namespace std;

int main()
{
	cout << "Hello CMake." << endl;
	Ck600* ck1 = Ck600::getInstance();
	Ck600* ck2 = Ck600::getInstance();
	cout << "ck1------>" << ck1 << endl;
	cout << "ck2------>" << ck2 << endl;
	return 0;
}

执行结果为


CMake的构建脚本CMakeLists.txt为

# CMakeList.txt: test 的 CMake 项目,在此处包括源代码并定义
# 项目特定的逻辑。
#
cmake_minimum_required (VERSION 3.8)

project ("test")

if (CMAKE_HOST_SYSTEM_NAME MATCHES "Linux")
    message("current platform: Linux ")
elseif (CMAKE_HOST_SYSTEM_NAME MATCHES "Windows")
    set(DEPS_POCO_INCLUDE_PATH E:\\poco-1.12.4-all\\poco\\include)
    set(DEPS_POCO_LIB_PATH E:\\poco-1.12.4-all\\lib64)
else ()
    message("current platform: unkonw platform")
endif ()

# 添加依赖头文件路径
include_directories(${DEPS_POCO_INCLUDE_PATH})

link_directories(${DEPS_POCO_LIB_PATH})

file(GLOB SERVER_CODE_SOURCES
        "${PROJECT_SOURCE_DIR}/*.h"
        "${PROJECT_SOURCE_DIR}/*.cpp"
        )

# 将源代码添加到此项目的可执行文件。
add_executable (test ${SERVER_CODE_SOURCES})

#target_link_libraries("PocoFoundation.lib")

if (CMAKE_VERSION VERSION_GREATER 3.12)
  set_property(TARGET test PROPERTY CXX_STANDARD 20)
endif()

set(CMAKE_MSVCIDE_RUN_PATH E:\\poco-1.12.4-all\\bin64)

# TODO: 如有需要,请添加测试并安装目标。

总结,打开SingletonHolder其实就是个C++的模板方法

template <class S>
class SingletonHolder
	/// This is a helper template class for managing
	/// singleton objects allocated on the heap.
	/// The class ensures proper deletion (including
	/// calling of the destructor) of singleton objects
	/// when the application that created them terminates.
{
public:
	SingletonHolder():
		_pS(0)
		/// Creates the SingletonHolder.
	{
	}

	~SingletonHolder()
		/// Destroys the SingletonHolder and the singleton
		/// object that it holds.
	{
		delete _pS;
	}

	S* get()
		/// Returns a pointer to the singleton object
		/// hold by the SingletonHolder. The first call
		/// to get will create the singleton.
	{
		FastMutex::ScopedLock lock(_m);
		if (!_pS) _pS = new S;
		return _pS;
	}

	void reset()
		/// Deletes the singleton object.
	{
		FastMutex::ScopedLock lock(_m);
		delete _pS;
		_pS = 0;
	}

private:
	S* _pS;
	FastMutex _m;
};


} // namespace Poco

可以看到就是个懒汉模式,只不过使用了模板方法来实现,并且没有实现DoubleCheck。

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表