LNK1169 найден один или несколько кратно определенных символов И LNK2005

Я столкнулся с этой проблемой, когда пытался скомпилировать свой код, я думал, что это может быть вызвано заголовочными файлами, включая друг друга. Но, насколько я могу судить, я не нашел никаких проблем с моими заголовочными файлами

Ошибка LNK1169: найден один или несколько кратно определенных символов Homework2 D:\05Development\04 C_C++\C\DS Alg class\Homework2\Debug\Homework2.exe 1

также есть ошибка, сообщающая, что функция Assert() была объявлена ​​в другом месте.

Ошибка LNK2005 "void __cdecl Assert(bool, класс std::basic_string, класс std::allocator >)" (?Assert@@YAX_NV?$ Basic_string@DU?$ Char_traits@D@std@@V?$ Allocator@D@2@@std@@@Z) уже определено в DataBase.obj Homework2 D:\05Development\04 C_C++\C\DS Alg class\Homework2\Homework2\dbTest.obj 1

вот структура моего кода:

функция

void Assert(bool val, string s)
{
    if (!val)
    {
        cout << "Assertion Failed!!: " << s << endl;
        exit(-1);
    }
}

находится в Constants.h

Виртуальный класс List включает Constants.h

#pragma once // List.h
#include "Constants.h"

Список массивов включает в себя класс List, в классе AList он вызывает функцию Assert

#pragma once //AList.h
#include "List.h"
...
Assert((pos >= 0) && (pos < listSize), "Position out of range");

В классе DataBase я создал член AList

private:
    AList<CData> set;

Заголовок выглядит так: #pragma Once #include "AList.h" #include "CData.h"

и CData.h выглядит так:

#pragma once
class CData
{
private:
    std::string m_name;

    int m_x;
    int m_y;

public:
    CData(std::string str = "null", int x = 0, int y = 0) : m_name(str), m_x(x), m_y(y) {}

    // Helper functions
    const std::string& GetName() const { return this->m_name; }
    const int& GetX() const { return this->m_x; }
    const int& GetY() const { return this->m_y; }
};

1 ответ

Решение

Когда вы строите свой проект, каждый файл.cpp компилируется отдельно в разные объектные файлы. once в #pragma once применяется только к компиляции одного файла.cpp, а не для проекта в целом. Таким образом, если файл.cpp содержит заголовок A и заголовок B, а заголовок B также включает заголовок A, то второе включение заголовка A будет пропущено.

Однако, если у вас есть другой файл.cpp, содержащий A, A будет снова включен в этот объектный файл - потому что #pragma once работает только при компиляции одного файла.cpp.

Инструкция #include буквально берет содержимое включенного файла и "вставляет" его в файл, который его включил. Вы можете попробовать это, посмотрев на вывод инструмента препроцессора C (cpp в gcc Набор инструментов). Если вы используете цепочку инструментов gcc, вы можете попробовать что-то вроде этого, чтобы увидеть файл после применения его включений:

cpp file.cpp -o file_with_includes.cpp

Если у вас есть функция в вашем заголовке, как Assert в вашем примере функция копируется в каждый файл.cpp, в который вы ее включаете.

Если у вас есть A.cpp и B.cpp, которые оба включают ваш файл Constants.h, каждый объектный файл (.o или.obj в зависимости от вашей среды) будет содержать копию вашей функции Assert. Когда компоновщик объединяет объектные файлы для создания двоичного файла, оба объектных файла объявляют, что они предоставляют определение для Assert, и компоновщик будет жаловаться, потому что он не знает, какой из них использовать.

Решение здесь заключается в том, чтобы встроить функцию Assert, например, так:

inline void Assert(bool val, string s)
{
    if (!val)
    {
        cout << "Assertion Failed!!: " << s << endl;
        exit(-1);
    }
}

или предоставить его тело в своем собственном файле.cpp, оставив в заголовке только прототип функции.

Constants.h:

void Assert(bool val, string s);

Constants.cpp:

void Assert(bool val, string s)
{
    if (!val)
    {
        cout << "Assertion Failed!!: " << s << endl;
        exit(-1);
    }
}

Имейте в виду, стандартная библиотека также предлагает assert(), который тоже хорошо работает. (см. https://en.cppreference.com/w/cpp/error/assert).

#include <cassert>
...
assert(is_my_condition_true());
assert(my_variable > 23);
// etc..

Просто имейте в виду, что заявленный в cassert работает только при компиляции для Debug и компилируется при сборке для Release (для ускорения выполнения), поэтому не помещайте в assert какой-либо код, имеющий побочные эффекты.

#include <cassert>
...
// Don't call functions with side effects.
// Thus function decreases a "count" and returns the new value
// In Release builds, this line will disappear and the decrement
// won't occur.
assert(myclass.decrement_count() > 0);
Другие вопросы по тегам