我的单身人士可多次被召唤

问题描述 投票:30回答:5

我已经实现了一个基于c ++ 11的单例。但是在某些情况下可以多次调用构造函数。

该类将被编译为静态库,并由其他lib(多个lib)使用。系统是一个多线程系统(在Android HAL级别运行)

/// .h文件:

class Logger
{
public:

    /// Return the singleton instance of Logger
    static Logger& GetInstance() {
        static Logger s_loggerSingleton;
        return s_loggerSingleton;
    }

private:

    /// Constructor
    Logger();
    /// Destructor
    ~Logger();
}

/// .cpp文件

Logger::Logger()
{
   ALOGE("OfflineLogger create");
}

Logger::~Logger()
{

}

它应该创建一次,例如:

03-21 01:52:20.785   728  4522 E         : OfflineLogger create

但是我可以看到它已被创建不止一次

03-21 01:52:20.785   728  4522 E         : OfflineLogger create
03-21 01:52:20.863   728  2274 E         : OfflineLogger create
03-21 01:52:20.977   728  2273 E         : OfflineLogger create
03-21 01:52:26.370   728  4522 E         : OfflineLogger create

问题:

  1. 我的单件设计有什么问题吗?这是一个线程安全的问题吗?
  2. 好像我的单身人士在一个范围内工作得很好,但每个包含我的单身人士的lib都会创建自己的单身人士,这样我的单身人士就不再是“单身人士”了。问题是由每个动态链接导致新的问题而“静态变量”变成“本地静态”吗?可能吗?如果是这样,如何解决?
android c++ singleton hal
5个回答
28
投票
  1. 我的单件设计有什么问题吗?这是一个线程安全的问题吗?

否。函数本地static变量的初始化保证是标准的线程安全。

  1. 好像我的单身人士在一个范围内工作正常,但每个包含我的单身人士的lib都会创建自己的单身人士,这样我的单身人士就不再是“单身人士”了。问题是由每个动态链接引起的,所以“staic veriable”变成“本地静态”吗?可能吗?如果是这样,如何解决

这是正确的结论。

而不是创建包含单例实现的静态库,使其成为动态库。


4
投票

单身人士很难,尤其是共享图书馆。

每个共享库都有一个非共享库的独立副本。没有额外的照顾,每个人都会有一份单身人士的副本。

为了拥有非平凡的单身人士,我必须做的就是

  1. 创建一个极低级别的库来帮助单身人士 - 称之为LibSingleton
  2. 创建一个知道单例类型的单例模板。它使用魔法静态向LibSingleton发送请求,其大小为typeid(T).name()密钥,并且类型擦除构造和销毁代码。 LibSingleton返回一个引用计数RAII对象。
  3. LibSingleton使用共享互斥锁返回先前构造的与名称/大小匹配的对象或构造它。如果它构造对象,则存储销毁代码。
  4. 当LibSingleton数据的最后一个引用计数句柄消失时,LibSingleton运行破坏代码并清除其无序映射中的内存。

这允许在几乎任何地方使用非常简单的单身人士。

template<class T>
class singleton {
public:
  static T& Instance() {
    static auto smart_ptr = LibSingleton::RequestInstance(
      typeid(T).name(),
      sizeof(T),
      [](void* ptr){ return ::new( ptr ) T{}; },
      [](void* ptr){ static_cast<T*>(ptr)->~T(); }
    );
    if (!smart_ptr)
      exit(-1); // or throw something
    return *static_cast<T*>(smart_ptr.get());
  }
protected:
  singleton() = default;
  ~singleton() = default;
private:
  singleton(singleton&&) = delete;
  singleton& operator=(singleton&&) = delete;
};

使用看起来像:

struct Logger : LibSingleton::singleton<Logger> {
  friend class LibSingleton::singleton<Logger>;
  void do_log( char const* sting ) {}
private:
  Logger() { /* ... */ }
};

2
投票

这是一个想法:不是使用单例,这在您的环境中既是一个难题,又已知在测试和维护方面存在问题,只需将代码设计为仅创建一个有问题的对象。


0
投票

静态变量应该移动到.cpp文件。

简单的方法是仅在.h中保留getInstance()的声明并将实现移动到.cpp文件。


-2
投票

可能是您的头文件被多次定义(如果多个文件是including这个头文件就是这种情况。尝试在头文件周围添加一个保护,以防止它被重新定义,如果它已经被定义一次。

根据您的C ++编译器,您实际上只需将#pragma once添加为文件中的第一行,就像这样

#pragma once
class Logger
{
    public:

    /// Return the singleton instance of Logger
    static Logger& GetInstance() {
    static Logger s_loggerSingleton;
    return s_loggerSingleton;
}

private:

    /// Constructor
    Logger();
    /// Destructor
    ~Logger();
}

预期的效果是最常用的替代方案,即添加像这样的宏定义

#ifndef LOGGER_H
#define LOGGER_H
class Logger
{
    public:

    /// Return the singleton instance of Logger
    static Logger& GetInstance() {
    static Logger s_loggerSingleton;
    return s_loggerSingleton;
}

private:

    /// Constructor
    Logger();
    /// Destructor
    ~Logger();
}
#endif LOGGER_H
© www.soinside.com 2019 - 2024. All rights reserved.