определение функций в заголовочных файлах
Если вы хотите поместить определения функций в файлы заголовков, представляется три различных решения:
- отметьте функцию как
inline
- отметьте функцию как
static
- помещать функцию в анонимное пространство имен
(До недавнего времени я даже не знал о №1.) Каковы различия между этими решениями, и когда я должен предпочесть, какой? Я в мире только для заголовков, поэтому мне действительно нужны определения в файлах заголовков.
Ответы
Ответ 1
static
и неназванные версии пространства имен становятся одинаковыми: каждый модуль перевода будет содержать свою собственную версию функции, а это означает, что при заданной статической функции f
указатель &f
будет отличаться в каждой единицы перевода, и программа будет содержать N разных версий f
(больше кода в двоичном формате).
Это не правильный подход, чтобы обеспечить функции в заголовке, это обеспечит п различную (точно равную) функцию. Если функция содержит static
локали, тогда будет N разных static
локальных переменных...
EDIT: чтобы сделать это более явным: если вы хотите предоставить определение функции в заголовке, не нарушая одно правило определения, правильный подход заключается в том, чтобы сделать функцию inline
.
Ответ 2
Насколько я знаю, в заголовочных файлах могут быть определены только inline
и шаблонные функции.
static
функции устаревают, и вместо этого следует использовать функции, определенные в неназванном пространстве имен (см. 7.3.1.1 p2). Когда вы определяете функцию в неназванном пространстве имен в заголовке, каждый исходный код, включающий этот заголовок (прямо или косвенно), будет иметь уникальное определение (см. 7.3.1.1 p1). Поэтому функции не должны определяться в неназванном пространстве имен в файлах заголовков (только в исходных файлах).
Стандарт, на который даны ссылки, относится к стандарту С++ 03.
РЕДАКТИРОВАТЬ:
Следующий пример демонстрирует, почему функции и переменные не должны быть определены в неназванное пространство имен в заголовках:
ops.hpp содержит:
#ifndef OPS_HPP
#define OPS_HPP
namespace
{
int a;
}
#endif
dk1.hpp содержит:
#ifndef DK1_HPP
#define DK1_HPP
void setValue();
void printValue();
#endif
dk1.cpp содержит:
#include "dk1.hpp"
#include "ops.hpp"
#include <iostream>
void setValue()
{
a=5;
}
void printValue()
{
std::cout<<a<<std::endl;
}
dk.cpp содержит:
#include "dk1.hpp"
#include "ops.hpp"
#include <iostream>
int main()
{
// set and print a
setValue();
printValue();
// set and print it again
a = 22;
std::cout<<a<<std::endl;
// print it again
printValue();
}
Скомпилируйте следующим образом:
g++ -ansi -pedantic -Wall -Wextra dk.cpp dk1.cpp
и выход:
5
22
5
ops переменная a
отличается для исходного файла dk1.cpp
и dk.cpp
Ответ 3
static
функции (эквивалентные анонимному пространству имен) получают разные копии для каждого ТУ. Если функция является повторной, это в основном идентично (некоторые компиляторы могут иметь разницу на уровне сборки), но если это не так, то для каждой ТУ она будет иметь разные статические данные. Встроенными функциями являются folded-, то есть они имеют только одну копию статических данных для каждого TU.
Ответ 4
Вы можете рассмотреть обертывание методов в классе вместо пространства имен. Объявите эти методы как статические и удалите конструктор класса, чтобы подчеркнуть, что это не объект, для которого создается экземпляр.
struct FooNamespace
{
FooNamespace() = delete;
static FooMethod1() {
...
}
static FooMethod2() {
...
}
};
Вы получаете то же общее поведение, что и в пространстве имен, только с одной реализацией.