我的单身可以被多次调用

共6个回答,已解决, 标签: android c++ singleton hal

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

该类将被编译到静态 lib, 并由其他这样的 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. 似乎我的单例工作在一个如此大的范围内很好, 但每一个这样的利布, 其中包括我的单例将创建自己的单例, 使我的单例不再是 "单例"。每个动态链接到新的问题是否都会导致 "静态变量" 变成 "局部静态"?有可能吗?如果是, 如何修复?

感谢您的任何输入!

第1个答案(采用)
  1. 我的单身设计有什么问题吗?这是一个线程安全问题吗?

不。通过标准, 函数局部变量的初始化 static 保证了线程安全。

  1. 似乎我的单例工作在一个如此大的范围内很好, 但每一个这样的利布, 其中包括我的单例将创建自己的单例, 使我的单例不再是 "单例"。每个动态连接到新的问题是否都会成为 "局部静态"? 有可能吗?如果是, 如何修复

这是正确的结论。

不要创建包含单例实现的静态库, 而是将其设置为动态库。

第2个答案

单身是很难的, 尤其是在共享库中。

每个共享库都有非共享库的独立副本。 如果不格外小心, 每个人都会有一份单身人士的副本。

为了有不平凡的单身, 我不得不做的是

  1. 创建一个极低级别的库, 以帮助处理单身人士----称之为 LibSingleton

  2. 创建一个知道单例类型的单例模板。 它使用神奇的静态方法向 LibSingleton 发送具有大小、 typeid(T).name() 键和类型擦除的构造和销毁代码的请求。 LibSingleton 返回计数 RAII 对象的引用。

  3. LibSingleton 使用共享互斥体返回以前构造的与名称大小匹配的对象或构造该对象。 如果它构造对象, 它将存储销毁代码。

  4. 当 LibSingleton 数据的最后一个引用计数句柄消失时, LibSingleton 将运行销毁代码并清理其无序映射中的内存。

这使得真正简单的单例几乎可以在任何地方使用。

template
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(ptr)->~T(); }
    );
    if (!smart_ptr)
      exit(-1); // or throw something
    return *static_cast(smart_ptr.get());
  }
protected:
  singleton() = default;
  ~singleton() = default;
private:
  singleton(singleton&&) = delete;
  singleton& operator=(singleton&&) = delete;
};

使用如下所示:

struct Logger : LibSingleton::singleton {
  friend class LibSingleton::singleton;
  void do_log( char const* sting ) {}
private:
  Logger() { /* ... */ }
};
第3个答案

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

第4个答案

使其成为源文件中的全局变量。这样, 即使使用静态链接, 您也只有一个定义:

我的单身设计有什么问题吗?这是一个线程安全问题吗?

前者的 "有点样", 后者的 "不"

似乎我的单例工作在一个如此大的范围内很好, 但每一个这样的利布, 其中包括我的单例将创建自己的单例, 使我的单例不再是 "单例"。每个动态连接到新的问题是否都会成为 "局部静态"?有可能吗?如果是, 如何修复

如果您的 Get 实例调用实际上是内联的, 它将为编译器提供创建多个定义的免费传递。如果您在源文件中声明它, 如果您多次将其链接为静态库, 它仍将导致麻烦。

此代码应正常工作

// Logger.cpp

Logger*    gLoggerInstance     = nullptr; // NOT STATIC
std::mutex gLoggerInstanceLock;  // NOT STATIC, only if you need thread-safety

Logger& Logger::GetInstance() {
    std::lock_guard(loggerInstanceLock); // only if you need thread-safety
    if(!loggerInstance) loggerInstance = new Logger();
    return *loggerInstance;
}

编辑: 只有当您将静态 libs 链接到多个 Dll, 然后链接到可执行文件中时, 才不使用静态库:

A.lib
|     \
X.dll  Y.dll
|     /
G.exe
第5个答案

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

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

第6个答案

可能是您的头文件被定义了多次 (如果多个文件是 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

相关问题

无法通过新项目从 jcenter 获取依赖项 [已关闭] 注意: 无法读取获取 [Ljava.lang.Object;@79d6c4df 的 kotlin 元数据 我的单身可以被多次调用