Поиск…


Умножённая функция

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

foo.h :

#ifndef FOO_H
#define FOO_H
#include <iostream>
void foo() { std::cout << "foo"; }
void bar();
#endif

foo.cpp :

#include "foo.h"
void bar() { std:: cout << "bar"; }

main.cpp :

#include "foo.h"
int main() {
    foo();
    bar();
}

В этой программе функция foo определена в заголовке foo.h , который включается дважды: один раз из foo.cpp и один раз из main.cpp . Поэтому каждая единица перевода содержит свое собственное определение foo . Обратите внимание, что включение foo.h в foo.h не мешает этому, поскольку foo.cpp и main.cpp оба отдельно включают foo.h Наиболее вероятным результатом попытки создания этой программы является ошибка времени соединения, определяющая foo как многократно определенную.

Чтобы избежать таких ошибок, следует объявлять функции в заголовках и определять их в соответствующих файлах .cpp , за некоторыми исключениями (см. Другие примеры).

Встроенные функции

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

Программа будет вести себя так, как если бы было одно определение функции.

foo.h :

#ifndef FOO_H
#define FOO_H
#include <iostream>
inline void foo() { std::cout << "foo"; }
void bar();
#endif

foo.cpp :

#include "foo.h"
void bar() {
    // more complicated definition
}

main.cpp :

#include "foo.h"
int main() {
    foo();
    bar();
}

В этом примере более простая функция foo определена внутри строки в файле заголовка, тогда как более сложная функциональная bar не является встроенной и определена в файле реализации. Оба foo.cpp перевода foo.cpp и main.cpp содержат определения foo , но эта программа хорошо сформирована, поскольку foo является встроенным.

Функция, определенная в определении класса (которая может быть функцией-членом или функцией друга), неявно встроена. Поэтому, если класс определен в заголовке, функции-члены класса могут быть определены в определении класса, хотя определения могут быть включены в несколько единиц перевода:

// in foo.h
class Foo {
    void bar() { std::cout << "bar"; }
    void baz();
};

// in foo.cpp
void Foo::baz() {
   // definition
}

Функция Foo::baz определяется вне строки, поэтому она не является встроенной функцией и не должна определяться в заголовке.

Нарушение ODR с помощью разрешения перегрузки

Даже при идентичных токенах для встроенных функций ODR может быть нарушен, если поиск имен не относится к одному и тому же объекту. рассмотрим func в следующем:

  • header.h

    void overloaded(int);
    inline void func() { overloaded('*'); }
    
  • foo.cpp

    #include "header.h"
    
    void foo()
    {
        func(); // `overloaded` refers to `void overloaded(int)`
    }
    
  • bar.cpp

    void overloaded(char); // can come from other include
    #include "header.h"
    
    void bar()
    {
        func(); // `overloaded` refers to `void overloaded(char)`
    }
    

У нас есть нарушение ODR, поскольку overloaded относится к различным объектам в зависимости от единицы перевода.



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow