【问题标题】:I am getting an error of redefinition while using extern header file使用外部头文件时出现重新定义错误
【发布时间】:2021-11-26 19:19:04
【问题描述】:

我在使用 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;

【问题讨论】:

  • 重新定义什么? 这个变量是什么?你能发布确切的错误文本吗?

标签: c++ extern


【解决方案1】:

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

对于像maxid这样的全局变量,extern表示它会有外部链接;也就是说,链接器知道并且可以在不同的源文件之间看到(翻译单元)。

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

然而,当你给它一个初始化器时,在这种情况下是=0(描述初始化的几种可能方式之一),那么它就变成了definition。它会导致分配存储并为该变量设置明确的位置。您不应该在标题中这样做,因为包含它的每个文件都将定义相同的变量。因此,在链接时您会得到多个,这是一个错误。

这样做的传统方法是将extern int x;放在标题中,以便每个人都知道x存在,然后将int x = 0;放在一个 CPP文件中,以便此变量存在于某处.写 extern int x = 0; 的意思是一样的,但是不习惯。

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

【讨论】:

    【解决方案2】:

    线

    extern int maxid = 0;

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

    extern int maxid;

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

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

    【讨论】:

      【解决方案3】:

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

      extern int maxid;
      

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

      【讨论】:

      • 这是初始化,不是赋值。另一种方法是将其声明为 inline 并从 CPP 文件中删除定义。
      • @JDługosz = 0 是赋值,整个表达式是初始化。我说 remove assignment 删除表达式的右侧。
      • 未分配。 en.cppreference.com/w/cpp/language/initialization = 0 是一个 等号 字符后跟一个表达式,但与 assignment 没有任何关系。没有“完整的表达式”,因为该行中唯一称为 expression 的是 0
      • @JDługosz 很高兴看到 SO 上的语言专家。
      • 初始化与赋值是我们一直试图深入学习 C++ 的人。保持区别并理解它们是不同的东西是重要的
      猜你喜欢
      • 2018-04-30
      • 2022-01-03
      • 2011-02-27
      • 1970-01-01
      • 2015-01-16
      • 1970-01-01
      • 2012-09-07
      • 2021-09-25
      • 2018-03-19
      相关资源
      最近更新 更多