LNK1169 พบสัญลักษณ์ที่กำหนดทวีคูณตั้งแต่หนึ่งตัวขึ้นไป และ LNK2005

ฉันพบปัญหานี้เมื่อพยายามรวบรวมโค้ด ฉันคิดว่าอาจเกิดจากไฟล์ส่วนหัวรวมเข้าด้วยกัน แต่เท่าที่ฉันสามารถบอกได้ ฉันไม่พบปัญหาใดๆ กับไฟล์ส่วนหัวของฉัน

ข้อผิดพลาด LNK1169 พบสัญลักษณ์ที่กำหนดทวีคูณตั้งแต่หนึ่งตัวขึ้นไป Homework2 D:\05Development\04 C_C++\C\DS Alg class\Homework2\Debug\Homework2.exe 1

นอกจากนี้ยังมีข้อผิดพลาดที่บอกฉันว่ามีการประกาศฟังก์ชัน Assert() ที่อื่นแล้ว

ข้อผิดพลาด 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) กำหนดไว้แล้วใน 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

รายการคลาสเสมือนประกอบด้วย 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 หนึ่งครั้ง #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; }
};

person Charlie Lee    schedule 15.10.2018    source แหล่งที่มา
comment
อย่าใส่โค้ดในไฟล์ส่วนหัว เว้นแต่จะเป็นโค้ดอินไลน์ในคำจำกัดความของคลาสหรือโค้ดเทมเพลต เฉพาะในไฟล์ .cpp เท่านั้น ที่นี่การใช้งานฟังก์ชัน Assert ไม่ควรอยู่ใน Constants.h ควรมีการประกาศเท่านั้น (void Assert(bool val, string s);)   -  person Jabberwocky    schedule 15.10.2018


คำตอบ (1)


เมื่อคุณสร้างโปรเจ็กต์ ไฟล์ .cpp แต่ละไฟล์จะถูกคอมไพล์แยกกันเป็นไฟล์อ็อบเจ็กต์ที่แตกต่างกัน once ใน #pragma once ใช้กับการรวบรวมไฟล์ .cpp ไฟล์เดียวเท่านั้น ไม่ใช่สำหรับโปรเจ็กต์โดยรวม ดังนั้น หากไฟล์ .cpp มีส่วนหัว A และส่วนหัว B และส่วนหัว B มีส่วนหัว A ด้วย ดังนั้นไฟล์ที่สองที่รวมส่วนหัว A จะถูกข้ามไป

อย่างไรก็ตาม หากคุณมีไฟล์ .cpp อื่นที่มี A ไฟล์ A จะถูกรวมไว้ในไฟล์อ็อบเจ็กต์นั้นอีกครั้ง เนื่องจาก #pragma once ใช้งานได้เฉพาะเมื่อรวบรวมไฟล์ .cpp ไฟล์เดียวเท่านั้น

คำสั่ง #include นำเนื้อหาของไฟล์ที่รวมมาอย่างแท้จริงและ "วาง" ลงในไฟล์ที่รวมไว้ คุณสามารถลองสิ่งนี้ได้โดยดูที่เอาต์พุตของเครื่องมือตัวประมวลผลล่วงหน้า C (cpp ใน gcc toolchain) หากคุณใช้ toolchain 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 ของตัวเอง โดยเหลือเพียงฟังก์ชันต้นแบบในส่วนหัว

ค่าคงที่h:

void Assert(bool val, string s);

ค่าคงที่.cpp:

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

โปรดทราบว่า Standard Library ยังมี assert() ซึ่งใช้ได้ดีเช่นกัน (ดู https://en.cppreference.com/w/cpp/error/assert< /ก>)

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

เพียงจำไว้ว่า assert ที่ประกาศใน 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);
person BareMetalCoder    schedule 15.10.2018