0
votes

I encountered this problem when I try to compile my code I thought it might be caused by header files including each other. But as far as I can tell I did not find any issues with my header files

Error LNK1169 one or more multiply defined symbols found Homework2 D:\05Development\04 C_C++\C\DS Alg class\Homework2\Debug\Homework2.exe 1

also, there's an error telling me that function Assert() has been declared elsewhere.

Error LNK2005 "void __cdecl Assert(bool,class std::basic_string,class std::allocator >)" (?Assert@@YAX_NV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z) already defined in DataBase.obj Homework2 D:\05Development\04 C_C++\C\DS Alg class\Homework2\Homework2\dbTest.obj 1

here's the structure of my code:

function

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

is in Constants.h

A virtual class List includes Constants.h

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

An array list includes List class, in the AList class it calls the Assert function

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

In the DataBase class I created a AList member

private:
    AList<CData> set;

header looks like this: #pragma once #include "AList.h" #include "CData.h"

and CData.h looks like this:

#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
Don't put code in header files, unless it's inline code in a class definition or templated code. Only in .cpp files. Here the Assert function implementation should not be in Constants.h, only the declaration should be there (void Assert(bool val, string s);)Jabberwocky

1 Answers

3
votes

When you build your project, each .cpp file gets compiled separately into different object files. The once in #pragma once only applies to the compilation of a single .cpp file, not for the project as a whole. Thus if a .cpp file includes header A and header B, and header B also includes header A, then the second include of header A will be skipped.

However, if you have another .cpp file that includes A, A will be included in that object file again -- because #pragma once only works when compiling a single .cpp file.

An #include statement literally takes the content of the included file and "pastes" it into the file that included it. You can try this by looking at the output of the C preprocessor tool (cpp in the gcc toolchain). If you are using the gcc toolchain, you can try something like this to see the file after its includes have been applied:

cpp file.cpp -o file_with_includes.cpp

If you have a function in your header, like Assert in your example, the function gets replicated into each .cpp file you include it in.

If you have A.cpp and B.cpp, that both include your Constants.h file, each object file (.o or .obj depending on your environment) will include a copy of your Assert function. When the linker combines the object files to create a binary, both object files will declare that they provide the definition for Assert, and the linker will complain, because it doesn't know which one to use.

The solution here is either to inline your Assert function, like this:

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

or to provide its body in its own .cpp file, leaving only the function prototype in the header.

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);
    }
}

Mind you, the Standard Library also offers assert(), which works nicely too. (see https://en.cppreference.com/w/cpp/error/assert).

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

Just keep in mind that the assert declared in cassert only works when compiling for Debug, and gets compiled out when building for Release (to speed up execution), so don't put any code in assert that has side effects.

#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);