Home  >  Q&A  >  body text

c++编译遇到重复定义

一直对编译这个事情不是太明白,想好好学习一下,今天自己写东西试验,遇到了这么个问题,想请教一下,先贴代码

// test.h
#ifndef JUST_FOR_TEST_H_
#define JUST_FOR_TEST_H_

struct Test {
  static int i;
  void t();
};

int Test::i = 0;

#endif
// test.cpp 
#include "test.h"

void Test::t() { }
// main.cpp
#include "test.h"

int main() {
  Test t;
  t.t();
}

本来觉得这个是很简单的,但是链接的时候报了错,提示是

ld: 1 duplicate symbol for architecture x86_64

将test.h中静态成员的定义放到cpp文件中就好了,但是我很不理解,不是使用了头文件保护吗?这样不是已经确保了一个头文件在一个工程中只会被包含一次吗?是我哪里的理解出了偏差了吗?希望各位高手可以指点一下,本人菜鸡,不是学计算机出身的,所以问的问题可能很傻,如果可以的话希望可以讲的详细一点,如果可以介绍两本学习的书就更好了,先谢过了!

天蓬老师天蓬老师2713 days ago495

reply all(1)I'll reply

  • 巴扎黑

    巴扎黑2017-04-17 15:41:16

    Short answer:

    headguard can only prevent header files from being included twice. The problem when you compile is that i is defined in different compilation units.

    When most IDEs are compiled, all source files will be compiled into target files separately, and finally the target files will be connected by the connector. The connection relationship between target files is inferred from the includes in the corresponding source files. Header files do not participate in compilation. The compilation script generated by the IDE here is almost clang++ main.cpp test.cpp. Of course, there are many compilation options in the project settings.

    In your example, the compiler will compile test.cpp and main.cpp to generate target files. The connector then joins the two target files. Since the static member variable i is defined in the header file, and both source files include this header file. The result is that i is defined once in two compilation units. A connection error occurs when finally connecting.

    Long answer:

    The link error you encountered is actually caused by an ODR violation.

    ODR Agreement:

    Only one definition of any variable, function, class type, enumeration type, or template is allowed in any one translation unit (some of these may have multiple declarations, but only one definition is allowed).

    One and only one definition of every non-inline function or variable that is odr-used (see below) is required to appear in the entire program (including any standard and user-defined libraries).

    But there are many special cases besides this. I won’t list them here (mainly because I can’t list them all...).

    Three concepts are very important: translation unit, definition and program.

    translation unit is a compilation unit. There are several compilation units determined by compilation instructions. The compilation result of the compilation unit is called an object file. definition is a definition. In this example, int Test::i = 0; is the definition of static member variable i. According to ODR, this definition can only appear once in the entire program. program is a program, which is linked by many target files.

    Compile instructions: clang++ main.cpp test.cpp -o main

    There are two compilation units main.cpp and test.cpp in this compilation. They will be compiled into target files separately. #include is a precompiled macro, which will introduce the contents of the corresponding file, and all precompiled instructions will be executed before the source code is compiled. In other words, the content of the compilation unit "test.cpp" (or the code received by the clang compiler) is:

    // test.cpp
    // test.h
    struct Test {
      static int i;
      void t();
    };
    
    int Test::i = 0;
    
    void Test::t() { }

    The contents of the compilation unit "main.cpp" are:

    // main.cpp
    // test.h
    struct Test {
      static int i;
      void t();
    };
    
    int Test::i = 0;
    
    int main() {
      Test t;
      t.t();
    }

    All macros are executed and comments will be ignored by the compiler.

    These two compilation units will be compiled into target files respectively, such as test.o and main.o. The compilation work is completed. Then the compiler will link the two object files test.o and main.o into one program. The ODR violation occurs during this linking .

    If int Test::i = 0; is defined in test.cpp. Then this definition will not appear in main.cpp, and the ODR will not be violated.

    reply
    0
  • Cancelreply