【问题标题】:Simple singleton pattern - Visual C++ assertion failure简单的单例模式 - Visual C++ 断言失败
【发布时间】:2012-06-22 09:58:09
【问题描述】:

我最近在 Visual C++ 2010 中编写了一个非常简单的记录器类,但是我遇到了一个问题。每次运行程序,都会出现调试断言失败。

Expression: _CrtIsValidHeapPointer(pUserData)

这就是我的班级的样子(基本上它只是从这里的答案C++ Singleton design pattern 中稍作修改):

class Logger
{
public:
    // Returns static instance of Logger.
    static Logger& getInstance()
    {
        static Logger logger; // This is where the assertion raises.
        return logger;
    }

    void logError(std::string errorText);

    // Destructor.
    ~Logger();

private:
    std::ofstream logFileStream;

    // The constructor is private to prevent class instantiating.
    Logger();
    // The copy constructor and '=' operator need to be disabled.
    Logger(Logger const&) { };
    Logger& operator=(Logger other) { };
};

而构造函数是:

Logger::Logger()
    : logFileStream(Globals::logFileName, std::ios_base::trunc)
{
    // (Tries to open the file specified in Globals for (re)writing.)
}

我发现我可以通过某种方式使用静态变量或方法来解决它,但我不明白这段代码有什么问题。有谁知道问题出在哪里?

编辑:仅供参考,调用此代码时会引发故障(第一次):

Logger::getInstance().logError("hello");

编辑2:这是logFileNameGlobals中的定义:

static const std::string logFileName = "errorLog.log";

【问题讨论】:

  • getInstance() 是否正确执行?你到logError()了吗?
  • 正确的实例化应该发生在静态方法中 - 我已经在代码中添加了注释。
  • 断言似乎发生在构造函数中。那是您没有显示的代码...
  • 这似乎没问题(只要它在 Logger 构造函数之前被初始化)——getInstance() 是否有任何机会从静态块中调用(例如,在计算静态变量的值时? )
  • 全局变量之间存在一些依赖关系(例如 logger 单例和 std::string logFileName。您确定它们以正确的顺序实例化吗?(请记住,如果每个变量位于不同的编译单元中,则顺序C++ 中没有定义构造方法。)

标签: visual-c++ singleton c++-cli assert


【解决方案1】:

我的猜测是,您正在从另一个全局变量的构造函数中调用 getInstance(),并遇到了臭名昭著的初始化顺序惨败 - 未指定 Globals::logFileName 是否已在其他翻译单元中的任何全局变量之前初始化。

一种解决方法是使用老式 C 字符串,该字符串将在调用任何全局构造函数之前进行静态初始化:

static const char * logFileName = "errorLog.log";

另一种可能性是通过函数访问它:

static std::string logFileName() {return "errorLog.log";}

我最喜欢的解决方案是完全删除全局实例,并将引用传递给任何需要它的对象;但有些人可能会觉得这相当乏味,特别是如果您已经有大量使用全局的代码。

【讨论】:

  • 是从构造函数调用的,但是构造函数的对象是在main()函数的开头创建的,所以不是全局的。感谢您的提示,但它并没有解决问题。
【解决方案2】:

C++/CLI 不是标准的 C++,其规则略有不同。您是否在使用 C++/CLI 托管代码? (/clr 编译器选项?)当将 C++(非托管)代码与 C++/CLI(托管)代码混合时,这看起来是一个常见问题。它与程序初始化和程序退出时托管和非托管构造和销毁的方式有关。删除析构函数对我有用 - 你可以在你的 Logger 类中这样做吗?

有关更多详细信息和可能的解决方法:

http://www.codeproject.com/Articles/442784/Best-gotchas-of-Cplusplus-CLI

http://social.msdn.microsoft.com/Forums/vstudio/en-US/fa0e9340-619a-4b07-a86b-894358d415f6/crtisvalidheappointer-fails-on-globally-created-object-within-a-static-llibrary?forum=vcgeneral

http://forums.codeguru.com/showthread.php?534537-Memory-leaks-when-mixing-managed-amp-native-code&p=2105565

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-02-19
    • 1970-01-01
    • 1970-01-01
    • 2013-11-22
    • 1970-01-01
    • 2013-08-20
    • 1970-01-01
    相关资源
    最近更新 更多