使用外部头文件时出现重定义错误

I am getting an error of redefinition while using extern header file

我在使用 extern 时遇到重新定义错误,但我也被告知,应该像这样使用 extern 变量,为什么我会收到此错误以及在这种情况下我应该如何使用 extern 才能正常工作? (即使我没有在 Tab.cpp 中指定它,我也可以使用这个变量,但是我得到一个或多个符号的错误,它被定义了 2 次。)

文件:

Tab.h:

#pragma once

#include "wx/wx.h"

class Tab : public wxFrame {
wxDECLARE_EVENT_TABLE();

void close(wxCommandEvent& evt);
void init();

public:
    Tab();
};

Tab.cpp:

#include "Tab.h"
#include "ids.h"
#include "wx/wx.h"

int maxid;

wxBEGIN_EVENT_TABLE(Tab, wxFrame)
    EVT_BUTTON(2, Tab::close)
wxEND_EVENT_TABLE()

Tab::Tab() : wxFrame(nullptr, maxid++, "ERIS 2") {
    init();
}

void Tab::close(wxCommandEvent &evt) { this->Close(); evt.Skip(); }

void Tab::init() {
    wxGridSizer* sizer = new wxGridSizer(10, 10, 0, 0);

    for(int x = 0; x < 10; ++x)
        for(int y = 0; y < 10; ++y) {
            sizer->Add(new wxButton(this, maxid, _(std::to_string(maxid))), wxEXPAND | wxALL);

            ++maxid;
        }
    

    this->SetSizer(sizer);
    sizer->Layout();
}

ids.cpp:

#include "ids.h"

std::vector<Object> ids;

Object& search(const char* name) {
    for(std::vector<Object>::iterator it = ids.begin(); it != ids.end(); *++it)
        if((*it).name == name)
            return *it;
}

Object& search(int id) {
    for(std::vector<Object>::iterator it = ids.begin(); it != ids.end(); *++it)
        if((*it).id == id)
            return *it;
}

void add(Object& obj) {
    ids.emplace_back(obj);
}

ids.h:

#pragma once

#include <vector>
#include "wx/wx.h"

struct Object {
    wxObject* obj;
    const char* name;
    int id;
};

Object& search(const char*);

Object& search(int);

void add(Object&);

extern std::vector<Object> ids;
extern int maxid = 0;

extern int maxid = 0;

在文件ids.h中是一个定义,因为它也初始化了变量。相反,它应该只包含一个声明:

extern int maxid;

定义应该在源文件 (.cpp) 中,而不是头文件 (.h)。头文件应该只包含变量声明,而不是定义。否则,如果多次包含头文件,或者源文件中已有定义,就会违反 one definition rule

在您的例子中,您已经在文件 Tab.cpp 中定义了变量。 int maxid; 行是一个定义,因为它没有使用 extern 关键字。如果你想初始化变量,你应该在那个文件中进行。

extern int maxid = 0; 当你分配一些东西时,这是一个定义,extern 变得毫无意义并被忽略。删除分配:

extern int maxid;

您在 Tab.cpp 中有定义,默认情况下它被分配为零作为全局变量。

定义声明声明 告诉编译器某物存在。 定义 是一个包含 所有 描述该事物所需信息的声明。

对于像maxid这样的全局变量,extern表示它会有外部linkage;也就是说,为 linker 所知并在不同的源文件(翻译单元)之间可见。

许多不同的翻译单元可以说 extern int maxid; 并且他们都只是说“好的,我知道这个符号,我最终会在某个地方找到它。”。所以,放入一个成为多个翻译单元的一部分的 header 很好。

然而,当你给它一个初始化器时,在本例中是 =0(描述初始化的几种可能方式之一),然后它变成了 definition。它会导致分配存储空间并为该变量设置一个明确的位置。您不应该在 header 中这样做,因为包含它的每个文件都将定义相同的变量。因此,在 link 次你得到了不止一个,这是一个错误。

传统的做法是把extern int x;放在header里,这样大家都知道x存在,然后把int x = 0;放在[=38=里]one CPP 文件,以便这个变量存在于某个地方。写作 extern int x = 0; 意思相同,但写作 un-idiomatic.

处理此问题的现代方法是使用为此目的创建的功能。将 inline int x = 0; 放入 header 文件中。这将在包含它的每个翻译单元中定义它,但是它们将被标记为 linker 理解它们都是相同的并且它应该只选择一个并且忽略其他人。