【问题标题】:Namespace function linker error when used in multiple classes在多个类中使用命名空间函数链接器错误
【发布时间】:2014-04-22 23:45:13
【问题描述】:

我只是在编写一个函数,我想在我的代码中使用它来调试而不抛出异常。我想要一个已定义的变量 DEBUG_MODE,当设置为 true 时会导致函数打印给定消息,而当设置为 false 时不打印任何内容。我希望它可用于导入文件而无需创建类的任何类,即我包含“Debug.h”并且可以调用 Debug::Log(string message)。

我读到了使用命名空间函数的优点,所以我就这样写了它,但也许我不理解命名空间函数的正确用法。当我在一个类中包含带有命名空间函数的头文件时,它工作得很好,当我在两个不同文件的两个类中使用它时,我得到一个链接器错误:

Error   1   error LNK2005: "void __cdecl Debug::Log(char const *)" (?Log@Debug@@YAXPBD0@Z) already defined in Foo.obj...

这是我的命名空间函数的代码:

#pragma once

#include <cstdio>

// IMPORTANT: Set this to true if you want debug logging, false otherwise
#define DEBUG_MODE true

namespace Debug
{
    void Log(const char* message)
    {
        if (DEBUG_MODE)
            printf(message);
    }

    void Log(const char* messageFormat, const char* parameter)
    {
        if (DEBUG_MODE)
            printf(messageFormat, parameter);
    }
}

现在,如果我将函数设为静态,那么一切正常,但我的印象是,在我的情况下,使用命名空间函数可以替代静态函数。如果可能的话,我还希望能够在另一个文件中定义静态函数。

我应该使用什么?静态函数?命名空间函数?如果是后者,那么如何在两个单独的类/文件中使用命名空间函数而不导致上述链接器错误?

【问题讨论】:

    标签: c++ static namespaces


    【解决方案1】:

    您所缺少的只是 inline 关键字,如下所示:

    inline void Log(const char* message)
    

    如果没有inline,则会为每个源文件(包括您的头文件)创建函数。

    static 会产生类似的结果,但对于这么小的函数,内联似乎更受欢迎。


    或者,如果您不想让编译器在所有文件中一次又一次地创建代码,您当然需要在一个 .cpp 文件中只实现一次,并且只在头文件中保留声明。

    但是,为了让 DEBUG_MODE 影响它,您需要这样做,例如像这样:

    #ifndef DEBUG_MODE
    
    // Dummy implementation
    inline void Log(...) {}
    
    #else
    
    // Implemented in Log.cpp
    void Log(const char* message);
    void Log(const char* messageFormat, const char* parameter);
    
    #endif
    

    【讨论】:

    • 我避免了内联,因为我将在我的代码中使用这个函数,并且内联基本上复制了我调用它的函数,这可能会生成一个大型可执行文件。虽然在这种情况下它对大型函数没有太大影响,但有什么办法可以解决这个问题吗?
    • 所以也许我不明白标头和实现的分离是如何工作的(由于很少使用 C++),但是当它在标头和 cpp 文件中时,编译器如何以不同的方式处理实现?我想了解为什么我应该按照你的建议去做。
    • 编译器被多次调用,大部分时间是针对您拥有的每个 .cpp 文件,并创建一个目标文件(.o 或 .obj)。然后链接器将所有目标文件连接到最终的可执行程序中。 -- 现在每个输入的 .cpp 文件都可以包含任意数量的#include——来自代码的不同部分、来自系统、来自 3rd 方库等。一次又一次地编译所有函数的内部工作是没有意义的,所以你只在头文件中留下声明,然后在一个 .cpp 文件中提供它的实现。然后编译并输出一次。
    【解决方案2】:

    尝试使它们内联。否则,您可以在单独的源文件中定义它们,然后在头文件中声明它们。

    如果您在标头中将其定义为非内联非静态函数并将其包含在程序的多个源文件中,则链接器将在多个目标文件中呈现相同函数的副本。

    有3种解决方案:

    1. 使它们内联。
    2. 将它们设为静态。
    3. 在它们自己的源文件中定义它们并且只在 头文件。

    将它们设为静态会导致名称不会被导出,因此它们只能在一个目标文件中知道。但是您仍然会有多个函数副本,但在每个使用它们的目标文件中每个副本只有一个。

    【讨论】:

    • 假设我选择后一个选项,我为什么要在单独的源文件中定义这样一个微不足道的函数?
    猜你喜欢
    • 2012-12-16
    • 1970-01-01
    • 2023-04-08
    • 2015-10-31
    • 2016-03-20
    • 1970-01-01
    • 1970-01-01
    • 2011-05-25
    • 1970-01-01
    相关资源
    最近更新 更多