2013-04-11 3 views
0

Недавно я столкнулся с проблемой.Ошибка ссылки на C++, переопределение символов

У меня есть три файла, Ах, B.cpp, C.cpp:

Ах

#ifndef __A_H__ 
#define __A_H__ 

int M() 
{ 
    return 1; 
} 

#endif // __A_H__ 

B.cpp

#include "A.h" 

C.cpp

#include "A.h" 

Как я перечислил три файла по MSVC, есть ошибка:

C.obj : error LNK2005: "int __cdecl M(void)" ([email protected]@YAHXZ) already defined in B.obj 

Легко понимание, как мы знаем, B.obj имеет символ с именем "M", также C.obj имеет "М". Здесь возникает ошибка.

Однако, если изменить метод М к классу, которые содержат метод M, как это показано ниже:

хиджры

#ifndef __A_H__ 
#define __A_H__ 

class CA 
{ 
public: 
    int M() 
    { 
     return 1; 
    } 
}; 

#endif // __A_H__ 

нет больше ошибок !! Может ли кто-нибудь сказать мне, что происходит?

+1

Вы не можете определить функцию в файлах заголовков, вы объявляете их в файлах заголовков. Однако классы могут быть объявлены в файлах заголовков. Все определения функций должны поступать в исходные файлы, за исключением нескольких строк. –

ответ

1

Если B.cpp и C.c включают хиджры, то оба компилируется с определением из M, так как объектные файлы будут содержать код для M. Когда компоновщик собирает все функции, он видит, что M определен в двух объектных файлах и не знает, какой из них использовать. Таким образом, компоновщик поднимает LNK2005.

Если поместить функцию M в класс декларации, то знаки компилятора/ручки M в качестве встроенной функции. Эта информация записывается в файл объекта. Линкер видит, что оба объектных файла содержат определение для inline версии CA::M, поэтому он предполагает, что оба они равны и случайно выбирают одно из двух определений.

Если бы вы написали

class CA { 
public: 
    int M(); 
}; 

int CA::M() 
{ 
    return 1; 
} 

это вызвало бы те же проблемы (LNK2005) в качестве начальной версии, потому что тогда CA::M не был бы рядный больше.

Как вы уже догадались, для вас есть два решения. Если вы хотите M быть встраиваемыми, а затем изменить код

__inline int M() 
{ 
    return 1; 
} 

Если вы не заботитесь о встраивании, то, пожалуйста, это стандартный способ и поставить функцию декларацию в заголовочном файл:

extern int M(); 

и поставить функцию определение в файл CPP (для Ah это было бы в идеале быть a.cpp):

int M() 
{ 
    return 1; 
} 

Обратите внимание, что extern не обязательно необходим в файле заголовка.

Другой пользователь предложил, чтобы писать

static int M() 
{ 
    return 1; 
} 

I'ld не рекомендуется. Это означало бы, что компилятор помещает M в оба ваших объектных файла и отмечает M как функцию, которая видна только в каждом объектном файле. Если компоновщик видит, что функция в B.cpp вызывает M, она находит M в B.obj и в C.obj. Оба имеют M, обозначенные как статические, поэтому компоновщик игнорирует M в C.obj и выбирает M от B.obj. И наоборот, если функция в C.cpp вызывает M, компоновщик выбирает M из C.obj. Вы получите несколько определений M, все с той же реализацией. Это пустая трата пространства.

+0

Это гораздо более подробный ответ и должен быть помечен как правильный, но из того, что я знаю, статический член должен создавать только одну копию функции в памяти, без потери пространства. Я также хочу предложить Quark использовать более C++ Подобный подход к этой проблеме. – IssamTP

+0

Спасибо за ваш ответ. Это очень подробно и помогает. – Quark

0

См. http://faculty.cs.niu.edu/~mcmahon/CS241/c241man/node90.html как защититься от ifdef. Вы должны начать с ifndef перед определением.

Редактировать: Ах нет, в то время как ваш охранник ошибается, это не проблема. Поставьте статику перед вашей функцией, чтобы она работала. Классы различаются, потому что они определяют типы.

0

Я не знаю, что находится под капотом, но если вам не нужен класс, я думаю, что компилятор автоматически добавит ключ «extern» к вашим функциям, поэтому вы получите ошибку, включая заголовок 2 раза.

Вы можете добавить статическое ключевое слово к методу M(), чтобы в момент компиляции была сохранена только одна копия этой функции.

Кстати: я вижу, что у вас есть #endif, но не #ifdef или #ifndef, это ошибка копирования/вставки?

+0

Извините, это ошибка копирования/вставки. – Quark

Смежные вопросы