【问题标题】:How can I be getting "already defined" linker errors here?我怎么能在这里得到“已经定义”的链接器错误?
【发布时间】:2013-09-17 14:24:55
【问题描述】:

我第一次尝试在 C++ 中进行单元测试,但我已经很多年没有使用过 C++(我目前主要是 C# 编码器)。看来我正在做一个正确的猪耳朵 - 我希望有人能引导我回到正义的道路上。我才刚刚开始,我真的很想使用可能的最佳实践来实施这些测试,所以欢迎任何和所有 cmets,即使我目前最担心我的链接器错误。

所以,我有一个整体解决方案“Technorabble”,包含子项目“CalibrationTool”和“CalibrationToolUnitTests”。

CalibrationTool 有一个 MathUtils.h 文件:

#ifndef __math_utils__
#define __math_utils__

#include "stdafx.h"
#include <vector>

namespace Technorabble
{
    namespace CalibrationTool
    {
        double GetDoubleVectorAverage(std::vector<double> v)
        {
            double cumulativeValue = 0;
            for(std::vector<double>::iterator iter = v.begin(); iter != v.end(); ++iter) 
            {
                cumulativeValue += *iter;
            }
            return cumulativeValue / v.size();
        }
    }; // end namespace CalibrationTool
};  // end namespace Technorabble

#endif // !__math_utils__

(但没有 .cpp 文件,因为我遇到了各种(有点相似)问题,让我的模板函数正常工作 - 所以我最终定义了内联)。

继续进行单元测试项目,我有一个 main.cpp:

#include "MathUtilsTest.h"

void RunMathUtilsTests();

int main()
{
    RunMathUtilsTests();

    // Other class tests will go here when I have things to test
}

void RunMathUtilsTests()
{
    MathUtilsTest* mathUtilsTest = new MathUtilsTest();
    mathUtilsTest->RunTests();
    delete mathUtilsTest;
}

最后,MathUtilsTest 类的标头和 cpp,同样非常简单:

.h:

#ifndef __MATH_UTILS_TEST__
#define __MATH_UTILS_TEST__

#include "CalibrationToolUnitTestsLogging.h"
#include "..\CalibrationTool\MathUtils.h"

class MathUtilsTest
{
public:
    MathUtilsTest();
    ~MathUtilsTest();

     bool RunTests();

private:    
    bool GetDoubleVectorAverageTest();

}; // end class MathUtilsTest

#endif

.cpp:

#include "MathUtilsTest.h"
#include <sstream>

bool MathUtilsTest::RunTests()
{
    return GetDoubleVectorAverageTest();

}

MathUtilsTest::~MathUtilsTest()
{

}

MathUtilsTest::MathUtilsTest()
{

}

bool MathUtilsTest::GetDoubleVectorAverageTest()
{
    bool passed = true;
    std::vector<double> values;
    for (int i = 1; i < 23; i++)
    {
        values.push_back(i);
    }
    // vector becomes: 1, 2, 3, 4, .....20, 21, 22.  Average is 11.5
    double expectedAverage = 11.5;
    double calculatedAverage = Technorabble::CalibrationTool::GetDoubleVectorAverage(values);
    if (calculatedAverage != expectedAverage)
    {
        std::ostringstream s;
        s << calculatedAverage;
        std::string avgString = s.str();
        CalibrationToolUnitTestsLogging::Write("Failed MathUtilsTest.GetDoubleVectorAverageTest:  " + avgString);
        passed = false;
    }
    else
    {
        CalibrationToolUnitTestsLogging::Write("Passed MathUtilsTest.GetDoubleVectorAverageTest");
    }

    return passed;
}

这一切对我来说似乎都很好,我正在使用#ifndef 等保护我的标题。但我仍然收到以下错误:

1) 错误 LNK1169:找到一个或多个多重定义符号

2) 错误 LNK2005: "double __cdecl Technorabble::CalibrationTool::GetDoubleVectorAverage(class std::vector >)" (?GetDoubleVectorAverage@CalibrationTool@Technorabble@@YANV?$vector@NV?$allocator@N@std@ @@std@@@Z) 已在 main.obj C:_SVN\Technorabble\Windows Software\CalibrationToolUnitTests\MathUtilsTest.obj 中定义

这怎么可能?谁能发现哪里出了问题?

【问题讨论】:

  • 与您的链接器问题无关,但您是否考虑过使用实际的单元测试框架?这通常是比自己滚动更好的解决方案。

标签: c++


【解决方案1】:

标头中定义的函数应标记为inline:

inline double GetDoubleVectorAverage(std::vector<double> v)
{
}

如果超过几行,请考虑将其移至实现文件。

pragmas 或包含守卫不能防止多重定义。

请注意,您应该通过 const 引用而不是按值传递 v

【讨论】:

  • 谢谢,内联似乎可以工作(尽管 .cpp 不能移动代码)。不过,我已经介绍了一个新的 const 问题。我的迭代器已失效。 “无法从 std::vector_const_iterator 转换为 std::vector_iterator。我会谷歌它。
  • @technorabble:如果向量是 const,那么您必须将迭代器类型更改为 std::vector&lt;double&gt;::const_iterator。在 C++11 中,您可以使用 auto 来节省输入。
  • 谢谢,已修复。将实现移动到 MathUtils.cpp 似乎需要它,如果不是在这里,那么对于更大的函数肯定是必要的,但是当我这样做时,我得到一个未解决的外部符号错误。有什么明显的原因会发生这种情况吗?
  • @mike-seymour:这是错误,不确定这是否能说明问题?错误 LNK2019:无法解析的外部符号“double __cdecl Technorabble::CalibrationTool::GetDoubleVectorAverage(class std::vector > const &)”(?GetDoubleVectorAverage@CalibrationTool@Technorabble@@YANABV?$ vector@NV?$allocator@N@std@@@std@@@Z) 在函数 "private: bool __thiscall MathUtilsTest::GetDoubleVectorAverageTest(void)" (?GetDoubleVectorAverageTest@MathUtilsTest@@AAE_NXZ) C:_SVN\Technorabble\ Windows 软件\CalibrationToolUnitTests\MathUtilsTest.obj
  • @technorabble:很可能,在将定义移入源文件后,您没有使用正确的命名空间对其进行限定。要么将其放在命名空间中(就像在标题中一样),要么使用全名Technorabble::CalibrationTool::GetDoubleVectorAverage。 (当然,还要确保源文件正在编译并与程序链接)。
【解决方案2】:

您正在标题中定义一个函数GetDoubleVectorAverage。这意味着它将在包含该标题的每个翻译单元(即每个源文件)中定义。如果您的程序包含多个这样的翻译单元,那么您将有多个定义 - 这是不允许的。

解决方案是:

  • 在函数定义中添加inline,放宽这个规则,允许多个相同的定义;或
  • 将函数定义移动到源文件中,并且只在头文件中声明。

我用#ifndef 保护我的标题

这只会防止标题在同一个翻译单元中被多次包含。它不会阻止包含多个单元。

此外,您不应该使用 reserved name 之类的 __math_utils__ 作为标头保护,即使互联网上充斥着这样做的狡猾代码示例。

在让我的模板函数正常工作时遇到了各种(有些相似的)问题

模板通常需要在头文件中定义,以使定义在使用时可用。函数模板隐含为inline,但普通函数(如这个)不是。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-09
    • 2012-10-06
    • 1970-01-01
    • 2018-05-06
    • 1970-01-01
    • 2014-06-28
    相关资源
    最近更新 更多