当前位置:   article > 正文

C++重定义问题_c++重定义怎么解决

c++重定义怎么解决

C++支持声明和定义分离,通常的做法是将声明放在头文件中、定义放在源文件中,通过包含头文件来引入声明。多次声明一个变量或函数不会有问题,但多次定义则会导致重定义错误。下面在两种不同场景下进行分析。

1.场景1

假设头文件foo.h声明了一个全局常量A和一个函数f

extern const int A;
int f(int x);
  • 1
  • 2

a.cpp包含foo.h并定义了常量A

#include "foo.h"

const int A = 2;
  • 1
  • 2
  • 3

f.cpp包含foo.h并定义了函数f

#include "foo.h"

int f(int x) {
    return x + A;
}
  • 1
  • 2
  • 3
  • 4
  • 5

主程序main.cpp调用了函数f

#include <iostream>

#include "foo.h"

using namespace std;

int main() {
    cout << f(5) << endl;
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

文件包含关系如下:

场景1-文件包含关系

此时程序能够输出正确结果:

$ g++ -o main a.cpp f.cpp main.cpp
$ ./main
7
  • 1
  • 2
  • 3

以上是正确的做法,下面分别尝试将常量A和函数f的定义移至头文件,看是否会有问题。

1.1 在头文件中定义常量

将常量A的定义移至foo.h:

const int A = 2;
int f(int x);
  • 1
  • 2

由于f.cpp和main.cpp都包含了foo.h,因此常量A实际定义了两次。

此时不再需要a.cpp,编译运行结果如下:

$ g++ -o main f.cpp main.cpp
$ ./main
7
  • 1
  • 2
  • 3

编译通过了,程序也输出了正确的结果,因此在不同源文件中重复定义常量不会导致编译错误(一般来说没有必要这样做,但用在switch语句case标签中的常量必须在声明的同时初始化,而不能用extern声明)。

1.2 在头文件中定义变量

将foo.h中的常量A改为变量:

int A = 2;
int f(int x);
  • 1
  • 2

再次编译:

$ g++ -o main f.cpp main.cpp
/tmp/ccaD1mlU.o:(.data+0x0): multiple definition of `A'
/tmp/ccMz5gGb.o:(.data+0x0): first defined here
collect2: error: ld returned 1 exit status
  • 1
  • 2
  • 3
  • 4

发现链接器报错:变量A重定义,即使在开头增加#pragma once#ifndef保护仍然会报错。

1.3 在头文件中定义函数

将函数f的定义移至foo.h中:

extern const int A;

int f(int x) {
    return x + A;
}
  • 1
  • 2
  • 3
  • 4
  • 5

编译结果如下:

$ g++ -o main a.cpp main.cpp
/tmp/cckzK4gH.o: In function `f(int)':
main.cpp:(.text+0x0): multiple definition of `f(int)'
/tmp/cc8Vn4bH.o:a.cpp:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status
  • 1
  • 2
  • 3
  • 4
  • 5

与变量的重定义类似,链接器报错函数重定义,即使使用#pragma once#ifndef保护仍然会报错。

2.场景2

场景1的文件包含关系是一个树状结构,下面考虑图状结构。假设头文件a.h、f.h、g.h分别声明了常量A、函数f和函数g,并且f.h包含了a.h,g.h包含了f.h,各文件的内容如下:

a.h

extern const int A;
  • 1

a.cpp

#include "a.h"

const int A = 2;
  • 1
  • 2
  • 3

f.h

#include "a.h"

int f(int x);
  • 1
  • 2
  • 3

f.cpp

#include "f.h"

int f(int x) {
    return x + A;
}
  • 1
  • 2
  • 3
  • 4
  • 5

g.h

#include "f.h"

int g(int x);
  • 1
  • 2
  • 3

g.cpp

#include "g.h"

int g(int x) {
    return f(x) * A;
}
  • 1
  • 2
  • 3
  • 4
  • 5

main.cpp

#include <iostream>

#include "f.h"
#include "g.h"

using namespace std;

int main() {
    cout << f(5) << endl;
    cout << g(5) << endl;
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

文件包含关系如下:

场景2-文件包含关系

程序运行结果如下:

$ g++ -o main a.cpp f.cpp g.cpp main.cpp
$ ./main
7
14
  • 1
  • 2
  • 3
  • 4

注:在这个例子中,头文件之间的包含关系理论上是没有必要的(只需在f.cpp中包含a.h中即可,而不需要在f.h中包含),这里只是为了说明问题。

下面仍然分别尝试将常量和函数的定义移至头文件中。

2.1 在头文件中定义常量

将常量A的定义移至a.h中,这次是编译器报错:

$ g++ -o main f.cpp g.cpp main.cpp
In file included from f.h:1:0,
                 from g.h:1,
                 from main.cpp:4:
a.h:1:11: error: redefinition of 'const int A'
 const int A = 2;
           ^
In file included from f.h:1:0,
                 from main.cpp:3:
a.h:1:11: note: 'const int A' previously defined here
 const int A = 2;
           ^
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

从错误信息可以分析出原因:在main.cpp中分别通过f.h和g.h 两次包含了a.h,从而在同一个源文件中两次定义了常量A,因此在编译期间就报错。

如果给a.h加上#pragma once#ifndef保护,则编译通过并正常运行:

$ g++ -o main f.cpp g.cpp main.cpp
$ ./main
7
14
  • 1
  • 2
  • 3
  • 4

这是因为编译器在main.cpp中展开g.h时,#pragma once阻止了再次包含a.h,因此main.cpp这个源文件中没有了重复定义(但f.cpp和g.cpp中仍然分别有一个常量A的定义)。

2.2 在头文件中定义变量

将a.h中的常量A改为变量,则不加#pragma once的编译结果如下:

$ g++ -o main f.cpp g.cpp main.cpp
In file included from f.h:1:0,
                 from g.h:1,
                 from main.cpp:4:
a.h:1:5: error: redefinition of 'int A'
 int A = 2;
     ^
In file included from f.h:1:0,
                 from main.cpp:3:
a.h:1:5: note: 'int A' previously defined here
 int A = 2;
     ^
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

编译器报错,原因同2.1节。

增加#pragma once的编译结果如下:

$ g++ -o main f.cpp g.cpp main.cpp
/tmp/ccEZX8VQ.o:(.data+0x0): multiple definition of `A'
/tmp/ccLS1UuT.o:(.data+0x0): first defined here
/tmp/ccMgRK4O.o:(.data+0x0): multiple definition of `A'
/tmp/ccLS1UuT.o:(.data+0x0): first defined here
collect2: error: ld returned 1 exit status
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

链接器报错,原因同1.2节。

2.3 在头文件中定义函数

将函数f的定义移至f.h中,类似地,不加#pragma once时编译器报错:

$ g++ -o main a.cpp g.cpp main.cpp
In file included from g.h:1:0,
                 from main.cpp:4:
f.h: In function 'int f(int)':
f.h:3:5: error: redefinition of 'int f(int)'
 int f(int x) {
     ^
In file included from main.cpp:3:0:
f.h:3:5: note: 'int f(int)' previously defined here
 int f(int x) {
     ^
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

加上#pragma once时链接器报错:

$ g++ -o main a.cpp g.cpp main.cpp
/tmp/ccqHxXhg.o: In function `f(int)':
main.cpp:(.text+0x0): multiple definition of `f(int)'
/tmp/cce5N9Pb.o:g.cpp:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status
  • 1
  • 2
  • 3
  • 4
  • 5

3.总结

  • 在同一个源文件中多次定义同一个常量、变量或函数时,编译器会报重定义错误。
  • 在不同源文件中多次定义同一个常量不会报错,而多次定义同一个变量或函数时链接器会报重定义错误。
  • #pragma once#ifndef保护只能解决同一个源文件中由于头文件传递包含导致的多次定义,不同源文件中的重复定义仍然会导致链接错误。
  • 定义全局常量、变量或函数的最好做法是在头文件中声明(常量和变量使用extern声明),仅在一个源文件中定义,从而可以避免重定义错误。
  • 例外:(1)模板函数必须在头文件中定义;(2)switch语句case标签使用的常量不能用extern声明。
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小惠珠哦/article/detail/838237
推荐阅读
相关标签
  

闽ICP备14008679号