如何在此处获取“已定义”的链接器错误?

时间:2013-09-17 14:24:55

标签: c++

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

所以,我有一个整体解决方案“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&gt;)”(?GetDoubleVectorAverage @ CalibrationTool @ Technorabble @@ YANV?$ vector @ NV?$ allocator @ N @ std @@@ std @@@ Z)已在main.obj中定义C:_SVN \ Technorabble \ Windows Software \ CalibrationToolUnitTests \ MathUtilsTest.obj

这怎么可能?谁能发现它出错的地方?

2 个答案:

答案 0 :(得分:5)

标题中定义的函数应标记为inline

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

如果它长于几行,请考虑将其移至实施文件。

pragma或包含警卫不能防止多种定义。

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

答案 1 :(得分:3)

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

解决方案是:

  • inline添加到函数定义中,以放宽此规则并允许多个相同的定义;或
  • 将函数定义移动到源文件中,并仅在标题中声明它。
  

我用#ifndef

保护我的标题

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

此外,你不应该使用像__math_utils__这样的inline作为标题保护,即使互联网上充斥着狡猾的代码这样做的例子。

  

我遇到各种(有些类似的)问题让我的模板功能正常工作

通常需要在头文件中定义模板,以便在使用时提供定义。函数模板隐式{{1}},但正常函数(如此函数)不是。