Заголовочный файл содержит тело функции, приведет ли к дублированию определения?
Я провел простой эксперимент, файл ".h" с определением класса и определением функции, как показано ниже:
$cat testInline.h
#pragma once
class C{
public:
void f(){}
};
void g(){}
Затем 2 пользователя этого.h файла:
$cat use01.cpp
#include"testInline.h"
void g01(){
g();
C obj1;
obj1.f();
}
$cat use02.cpp
#include"testInline.h"
int main(){
g();
C obj2;
obj2.f();
return 0;
}
Я собираю их вместе и получаю ошибку:
$g++ use01.cpp use02.cpp
duplicate symbol __Z1gv in:
/var/folders/zv/b953j0_55vldj97t0wz4qmkh0000gn/T/use01-34f300.o
/var/folders/zv/b953j0_55vldj97t0wz4qmkh0000gn/T/use02-838e05.o
ld: 1 duplicate symbol for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Выглядит очень странно: я однажды использовал "#pragma", но до сих пор не могу остановить компилятор от сообщения дублированного определения g()(__Z1gv как искажение имени)
Затем я изменил определение testInline.h->g() так:
inline void g(){}
Ну, это компилируется. Разве в C++ ключевое слово "inline" в принципе бесполезно, потому что компиляторы решат, будет ли оно встроено в функцию или нет?
И почему C::f() с кодом в.h файле не сообщает о дублировании, а функция в стиле C g () -? И почему класс C не должен "добавлять" inline "для своей функции"f()", а g () должен использовать"inline"?
Надеюсь, я четко сформулировал свой вопрос. Спасибо за вашу помощь.
2 ответа
Я использовал "#pragma один раз",
Да вы сделали. И каждый из двух блоков перевода эффективно обрабатывал заголовочный файл ровно один раз. Каждый из них сделал бы это даже без прагмы, поскольку каждый блок перевода включает заголовочный файл только один раз.
#pragma once
не означает "включать этот заголовочный файл только в одну из компилируемых единиц перевода". Это означает "включать этот файл заголовка один раз на единицу перевода, даже если единица перевода прямо или косвенно включает файл заголовка два или более раз". Таким образом, каждая единица перевода включала файл заголовка и определяла функции / методы из самого файла заголовка. Так как одна и та же функция или метод в конечном итоге были определены обеими единицами перевода, вы получили дубликат во время соединения.
Разве в C++ ключевое слово "inline" в принципе бесполезно, потому что компиляторы решат, будет ли оно встроено в функцию или нет?
Это правда, что компилятор решает, будет ли функция встроена или нет. Тем не менее inline
Ключевое слово указывает, обрабатывается ли определение функции так, как если бы оно было логически встроено для каждого ее использования, а не определено фактически. Таким образом, используя inline
Ключевое слово не приводит к дублированию определений, так как, по логике, функция вставляется в строку при каждой ссылке.
Это правда, что произойдет ли это на самом деле, или же компилятор выдаст не встроенный код, зависит от компилятора. Однако C++ требует, чтобы функция была скомпилирована "как если бы" была встроена; таким образом, даже если компилятор решит не включать функцию, он должен предпринять все необходимые шаги, чтобы гарантировать, что дублированные не встроенные копии функции не приведут к некорректной программе.
И почему C::f() с кодом в.h файле не сообщает о дублировании,
Поскольку метод класса, определяемый внутри определения класса, фактически является встроенным определением, даже если inline
Ключевое слово не указано явно.
inline
Ключевое слово не бесполезно. Просто он не обязательно контролирует, является ли функция встроенной.
inline
Ключевое слово помечает функцию как возможно определенную в нескольких единицах перевода. (Определение и значение должны быть одинаковыми во всех них.) Вы должны пометить функцию, определенную в заголовочном файле, как inline
,
Функция, определенная в определении класса, как ваш C::f
, автоматически считается "inline
".