多定义错误C ++ [英] multiple definition error c++

查看:104
本文介绍了多定义错误C ++的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的"Headers.h"文件包含基本的c ++标头

#include <iostream>
#include <cstring>
// and many header files.

为文件存在检查写一个函数定义,并将其保存在"common_utility.h"中-ifFileExist()

common_utility.h

bool ifFileExist()
{
   // ... My code
}

A类的书面代码 classA.h

class A
{
// Contains class A Declarations.

};

classA.cpp

// Contains
#include "Headers.h"
#include "common_utility.h"
#include "classA.h"

// class A Method definition

B类的书面代码 我正在使用B类中的A类.

classB.h

 class B
    {
// Contains class A Declarations.
}

classB.cpp

// Contains
#include "Headers.h"
#include "common_utility.h"
#include "classA.h"
#include "classB.h"

// class B Method definition
// calling the function ifFileExist() in class B also.

主程序的写代码 main.cpp

// Contains
#include "Headers.h"
#include "common_utility.h"
#include "classA.h"
#include "classB.h"

// I am using class A and Class B in main program
// calling the function ifFileExist() in Main program also.

当我将整个程序编译为

g ++ -std = c ++ 0x classA.cpp classB.cpp main.cpp -o main

我收到以下错误.

在函数ifFileExist()': classB.cpp:(.text+0x0): multiple definition of ifFileExist()中 /tmp/ccHkDT11.o:classA.cpp:(.text+0x2b6e):首先在此处定义

因此,我在Headers.h中取消了ifFileExist()函数作为外部函数.

extern bool ifFileExist();

但是我仍然遇到相同的错误.

我在每个.cpp文件中都包含"Headers.h".该文件包含基本的c ++库.但是我没有得到该头文件的多个定义错误. 但是只有在我自己的函数中,我才收到错误多重定义".

我想在需要使用"common_utility.h"文件时使用它.如果我不需要在主程序中使用common_utility函数,那么我就不应该包含它.

我希望我的程序在以下所有情况下运行.

g ++ -std = c ++ 0x classA.cpp main.cpp -o main
g ++ -std = c ++ 0x classB.cpp> main.cpp -o main
g ++ -std = c ++ 0x classA.cpp classB.cpp main.cpp -o main

在任何情况下我都不会出现多个定义错误.我现在应该怎么办?

解决方案

由于我找不到该问题的完整(我认为)重复,因此,我将写一个(希望)权威且完整的答案.

什么是一个定义规则,我为什么要关心

一个定义规则,通常称为ODR,是一个规定(简化)的规则,规定程序中使用的任何实体(非正式术语)都应定义一次,并且只能定义一次.多次定义的实体通常会导致编译或链接器错误,但有时可能会被编译器检测不到,并导致非常难以跟踪的错误.

我不会在这里正式定义实体,但可以将其视为函数,变量或类.在继续之前,应该非常清楚地了解C ++中的定义声明之间的区别,因为虽然禁止了双重定义,但通常不可避免地会出现双重声明.

定义与声明

在代码中使用的每个实体都应在给定的翻译单元中进行声明(翻译单元通常是cpp源文件以及其中包含的所有头文件,直接或通过其他头文件间接获得) .实体的声明方式因实体本身而异.有关如何声明不同类型的实体,请参见下文.实体通常在头文件中声明.由于最复杂的应用程序中包含一个以上的转换单元(一个以上的cpp文件),并且不同的cpp文件通常包含相同的标头,因此一个应用程序可能会为使用的许多实体提供多个声明.就像我上面说的那样,这不是问题.

应用程序中使用的每个实体都必须定义一次,并且只能一次. 应用程序"一词在这里使用得比较宽松-例如,库(静态和动态)中可以包含未定义的实体(此时通常称为符号),而链接为使用动态库的可执行文件可以也有未定义的符号.相反,我指的是该应用程序是最终运行的 something ,所有库都已静态或动态链接到其中,并且符号已解析.

还值得注意的是,每个定义也都充当声明,这意味着,每当定义某些内容时,您也就声明了同一件事.

与声明一样,定义实体的方式因实体类型而异.这是一种可以根据实体的类型声明/定义3种基本类型的实体的方法-变量,类和函数.

变量

使用以下构造声明变量:

extern int x;

这将声明变量x.它没有定义!下面的代码将被编译为OK,但是由于没有定义符号而试图在没有任何其他输入文件的情况下链接它(例如,使用g++ main.cpp)将产生链接时错误:

extern int x;
int main() {
    return x;
}

以下代码定义变量x:

int x;

如果将这一行放入文件x.cpp中,并使用g++ x.cpp main.cpp -o test从上面将文件与main.cpp编译/链接在一起,它将进行编译和链接而不会出现问题.您甚至可以运行生成的可执行文件,如果要在运行可执行文件后检查退出代码,您会发现它为0.(因为全局变量x将默认初始化为0).

功能

通过提供函数原型来声明函数.典型的函数声明如下所示:

double foo(int x, double y);

此结构声明一个函数foo,返回double并接受两个参数-一个为int类型,另一个为double类型.该声明可以出现多次.

以下代码定义上面提到的foo:

void foo(int x, double y) {
    return x * y;
}

定义在整个应用程序中只能出现一次.

函数定义对变量定义有一个额外的怪癖.如果将foo的上述定义放入头文件foo.h中,则该文件又将包含在两个cpp文件1.cpp2.cpp中,这两个文件与g++ 1.cpp 2.cpp -o test一起编译/链接在一起,您将拥有一个链接器错误,表示foo()被定义两次.可以通过使用以下形式的foo声明来防止这种情况:

inline void foo(int x, double y) {
    return x * y;
}

请注意inline.它告诉编译器的是foo可以被多个.cpp文件包含,并且此包含不应会产生链接器错误.编译器有几种方法可以实现这一目标,但是可以依靠它来完成任务.请注意,在同一翻译单元中两次定义都将是一个错误!例如,以下代码将产生编译器错误

inline void foo() { }
inline void foo() { }

值得注意的是,该类中定义的任何类方法都是隐式内联的,例如:

class A {
public:
    int foo() { return 42; }
};

这里定义了A :: foo()inline.

Classess

Classess通过以下构造进行声明:

class X;

在声明声明上的类X(此时X正式称为不完整类型),因此可以在有关其内容的信息(例如,它的大小或不需要成员.例如:

X* p; // OK - no information about class X is actually required to define a pointer to it
p->y = 42; // Error - compiler has no idea if X has any member named `y`

void foo(X x); // OK - compiler does not need to generated any code for this

void foo(X x) { } // Error - compiler needs to know the size of X to generate code for foo to properly read it's argument
void bar(X* x) { } // OK - compiler needs not to know specifics of X for this

类的定义是众所周知的,并遵循以下结构:

class X {
   public:
   int y;
};

这将定义X类,现在可以在任何上下文中使用它.重要说明-类定义在每个trannlation单元中必须唯一,但在每个应用程序中不必唯一.也就是说,每个翻译单元只能定义一次X,但是它可以在链接在一起的多个文件中使用.

如何正确遵守ODR规则

只要在结果应用程序中多次定义同一实体,就会发生所谓的 ODR违规.在大多数情况下,链接器会看到违规行为并进行投诉.但是,在某些情况下,违反ODR的操作不会中断链接,而是会导致错误.例如,当将定义全局变量X的相同.cpp文件放入应用程序库和动态库中时,可能会发生这种情况,动态库按需加载(使用dlopen). (您确实花了几天的时间来尝试跟踪由于此而发生的错误.)

更常见的违反ODR的原因是:

同一实体在同一范围内的同一文件中定义了两次

int x;
int x; // ODR violation

void foo() {
   int x;
} // No ODR violation, foo::x is different from x in the global scope

预防:不要这样做.

同一实体定义了两次(应在声明时声明)

(in x.h)
int x;

(in 1.cpp)
#include <x.h>
void set_x(int y) {
   x = y;
}

(in 2.cpp)
#include <x.h>
int get_x() {
    return x;
}

尽管上面代码的智慧充其量是可疑的,但它在说明ODR规则方面起到了作用.在上面的代码中,变量x应该在两个文件1.cpp和2.cpp之间共享,但是编码不正确.相反,代码应如下:

(in x.h)
extern int x; //declare x

(in x.xpp)
int x; // define x

// 1.cpp and 2.cpp remain the same

预防 知道你在做什么.在声明实体时声明它们,不要定义它们. 如果在上面的示例中,我们将使用函数而不是变量,如下所示:

(in x.h)
int x_func() { return 42; }

我们将有一个可以通过两种方式解决的问题(如上所述).我们可以使用inline函数,也可以将定义移至cpp文件:

(in x.h)
int x_func();

(in x.cpp)
int x_func() { return 42; } 

相同的头文件包含两次,导致相同的类定义了两次 这是一个有趣的.想象一下,您有以下代码:

(in a.h)
class A { };

(in main.cpp)
#include <a.h>
#include <a.h> // compilation error!

上面的代码很少以书面形式出现,但是通过中间文件将相同的文件包含两次很容易:

(in foo.h)
#include <a.h>

(in main.cpp)
#include <a.h>
#include <foo.h>

Prevention 对此的传统解决方案是使用所谓的 include Guards ,即一种特殊的预处理程序定义,它可以防止双重包含.在这方面,应按以下方式重做a.h:

(in a.h)
#ifndef INCLUDED_A_H
#define INCLUDED_A_H

class A { };

#endif

上面的代码将防止一次将a.h包含在同一个翻译单元中,因为INCLUDED_A_H将在首次包含之后定义,并且在所有随后的#ifndef中都将失败.

一些编译器提供了其他控制包含的方法,但是到目前为止,包括守卫仍然是在不同的编译器之间统一地进行控制的方法.

My 'Headers.h' file includes basic c++ Headers

#include <iostream>
#include <cstring>
// and many header files.

wrote a function definition for file exist check and saved it in 'common_utility.h' - ifFileExist()

common_utility.h

bool ifFileExist()
{
   // ... My code
}

Wrote code for Class A classA.h

class A
{
// Contains class A Declarations.

};

classA.cpp

// Contains
#include "Headers.h"
#include "common_utility.h"
#include "classA.h"

// class A Method definition

Wrote Code for Class B I am using class A in Class B.

classB.h

 class B
    {
// Contains class A Declarations.
}

classB.cpp

// Contains
#include "Headers.h"
#include "common_utility.h"
#include "classA.h"
#include "classB.h"

// class B Method definition
// calling the function ifFileExist() in class B also.

wrote code for main program main.cpp

// Contains
#include "Headers.h"
#include "common_utility.h"
#include "classA.h"
#include "classB.h"

// I am using class A and Class B in main program
// calling the function ifFileExist() in Main program also.

When I compile the whole program as

g++ -std=c++0x classA.cpp classB.cpp main.cpp -o main

I am getting the following error.

In function ifFileExist()': classB.cpp:(.text+0x0): multiple definition ofifFileExist()' /tmp/ccHkDT11.o:classA.cpp:(.text+0x2b6e): first defined here

So I decleard ifFileExist() function in Headers.h as extern.

extern bool ifFileExist();

But still I am getting the same error.

I am including 'Headers.h' in every .cpp file. That file contains basic c++ libraries. But I didn't get any mulitple definition error for that header files. But only in my own function, I am getting the error 'multiple definition'.

I want to use 'common_utility.h' file, when ever I need to use it. If I doesn't need to use the common_utility functions in my main program, simply I should not include it.

I want my program to run in the every following cases.

g++ -std=c++0x classA.cpp main.cpp -o main
g++ -std=c++0x classB.cpp> main.cpp -o main
g++ -std=c++0x classA.cpp classB.cpp main.cpp -o main

I shouldn't get mulitple definition error at any cases. What Should I do now?

解决方案

Since I could not find any complete (in my view) duplicate for this question, I am going to write a (hopefully) authoritive and complete answer.

What is One Definition Rule and why should I care

A One Definition Rule, usually dubbed ODR, is a rule which states (simplified) that any entity (informal term) used in the program should be defined once, and only once. An entity which is defined more than once is often causing a compilation or linker error, but sometimes can be left undetected by the compiler and lead to very hard-to-trace bugs.

I am not going to formally define entity here, but one can think of it as a function, variable or class. Before going further, one should very clear understand the difference between definition and declaration in C++, since while double definition is prohibited, double declaration is usually unavoidable.

Definition vs. declaration

Every entity used in the code should be declared in the given translation unit (translation unit is usually a cpp source file together with all header files included in it, directly or indirectly through other header files). The way an entitty is declared differes based on the entity itself. See below on how to declare different types of entities. Entities are often declared in header files. Since most complex application has more than one translation unit in it (more than one cpp file), and different cpp files often include the same headers, an application is likely to have multiple declarations for many entities used. Like I said above, this is not a problem.

Every entity used in the application, must be defined once and only once. The term 'application' is used a bit loosely here - for example, libraries (both static and dynamic) can have entities (at this point usually called symbols) left undefined within them, and an executable which was linked to use a dynamic library can have a symbol undefined as well. Instead, I refer to the application is an ultimate running something, after all the libraries have been statically or dynamically linked into it, and symbols resolved.

It is also worth noting that every definition serves as a declaration as well, meaning, that whenever you define something, you are also declaring the same thing.

As with declaration, the way to define an entity differes by the type of entity. Here is how one can declare/define 3 basic types of entities - variables, classes and functions - based on it's type.

Variables

Variables are declared using following construct:

extern int x;

This declares a variable x. It does not define it! A following piece of code will be compiled OK, but an attempt to link it without any other input files (for example, with g++ main.cpp) will produce a link-time error due to undefined symbols:

extern int x;
int main() {
    return x;
}

The following piece of code defines variable x:

int x;

If this single line were to be put into file x.cpp, and this file compiled/linked together with main.cpp from above with g++ x.cpp main.cpp -o test it would compile and link without problems. You could even run resulting executable, and if you are to check exit code after the executable was run, you'd notice it is 0. (Since global variable x would be default-initialized to 0).

Functions

Functions are declared by providing their prototypes. A typical function declaration looks like following:

double foo(int x, double y);

This construct declares a function foo, returning double and accepting two arguments - one of type int, another of type double. This declaration can appear multiple times.

Following code defines above mentioned foo:

void foo(int x, double y) {
    return x * y;
}

This definition can only appear once in the whole application.

Function definition has an additional quirk to variable definition. If above definition of foo were to put into header file foo.h, which in turn would be included by two cpp files 1.cpp and 2.cpp, which are compiled/linked together with g++ 1.cpp 2.cpp -o test you would have a linker error, saying that foo() is defined twice. This might be prevented by using following form of foo declaration:

inline void foo(int x, double y) {
    return x * y;
}

Note inline there. What it tells compiler is that foo can be included by multiple .cpp files, and this inclusion should not produce linker error. Compiler have several options on how to make this happen, but it can be relied upon to do it's job. Note, it would still be an error to have this definition twice in the same translation unit! For example, following code will produce a compiler error

inline void foo() { }
inline void foo() { }

It is worth noting, that any class method defined within the class is implictly inline, for example:

class A {
public:
    int foo() { return 42; }
};

Here A::foo() is defined inline.

Classess

Classess are declared by following construct:

class X;

Above declaration declares class X (and at this point X is formally called an incomplete type), so that it can be used when information about it contents, such as it's size or it's members is not needed. For example:

X* p; // OK - no information about class X is actually required to define a pointer to it
p->y = 42; // Error - compiler has no idea if X has any member named `y`

void foo(X x); // OK - compiler does not need to generated any code for this

void foo(X x) { } // Error - compiler needs to know the size of X to generate code for foo to properly read it's argument
void bar(X* x) { } // OK - compiler needs not to know specifics of X for this

A definition of class is well-known to everybody, and follows this construct:

class X {
   public:
   int y;
};

This makes a class X defined, and now it can be used in any context. An important note - class definition has to be unique per tralnlation unit, but does not have to be unique per application. That is, you can have X defined only once per translation unit, but it can be used in multiple files linked together.

How to properly follow ODR rules

Whenever a same entity is defined more than once in the resulting application, so-called ODR violation happenes. Most of the time, a linker will see the violation and will complain. However, there are cases when ODR violation does not break linking and instead causes bugs. This might happen, for example, when the same .cpp file defining a global variable X is put into both application and dynamic library, which is loaded on demand (with dlopen). (Yours trully spent a couple of days trying to trace a bug happened because of that.)

A more conventional causes of ODR violations are:

Same entity defined twice in the same file in the same scope

int x;
int x; // ODR violation

void foo() {
   int x;
} // No ODR violation, foo::x is different from x in the global scope

Prevention: don't do this.

Same entity defined twice, when it was supposed to be declared

(in x.h)
int x;

(in 1.cpp)
#include <x.h>
void set_x(int y) {
   x = y;
}

(in 2.cpp)
#include <x.h>
int get_x() {
    return x;
}

While the wisdom of above code is questionable at best, in serves a point of illustrating ODR rule. In the code above, variable x is supposed to be shared between two files, 1.cpp and 2.cpp, but was coded incorrectly. Instead, the code should be following:

(in x.h)
extern int x; //declare x

(in x.xpp)
int x; // define x

// 1.cpp and 2.cpp remain the same

Prevention Know what you are doing. Declare entities when you want them declared, do not define them. If in the example above we'd use function instead of the variable, like following:

(in x.h)
int x_func() { return 42; }

We would have a problem which could be solved in two ways (as mentioned above). We could use inline function, or we could move definition to the cpp file:

(in x.h)
int x_func();

(in x.cpp)
int x_func() { return 42; } 

Same header file included twice, causing the same class defined twice This is a funny one. Imagine, you have a following code:

(in a.h)
class A { };

(in main.cpp)
#include <a.h>
#include <a.h> // compilation error!

The above code is seldom appearing as written, but it is quite easy to have the same file included twice through the intermediate:

(in foo.h)
#include <a.h>

(in main.cpp)
#include <a.h>
#include <foo.h>

Prevention Traditional solution to this is to use so-called include guards, that is, a special preprocessor definitions which would prevent the double-inclusion. In this regard, a.h should be redone as following:

(in a.h)
#ifndef INCLUDED_A_H
#define INCLUDED_A_H

class A { };

#endif

The code above will prevent inclusion of a.h into the same translation unit more than once, since INCLUDED_A_H will become defined after first inclusion, and will fail #ifndef on all subsequent ones.

Some compilers expose other ways to control inclusion, but to date include guards remain the way to do it uniformely across different compilers.

这篇关于多定义错误C ++的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆