Szukaj…


Wielokrotnie zdefiniowana funkcja

Najważniejszą konsekwencją reguły One Definition jest to, że funkcje nieliniowe z zewnętrznym łączeniem powinny być definiowane tylko raz w programie, chociaż mogą być deklarowane wielokrotnie. Dlatego takich funkcji nie należy definiować w nagłówkach, ponieważ nagłówek może być dołączany wiele razy z różnych jednostek tłumaczeniowych.

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();
}

W tym programie funkcja foo jest zdefiniowana w nagłówku foo.h , który jest zawarty dwukrotnie: raz z foo.cpp i raz z main.cpp . Każda jednostka tłumacząca zawiera zatem własną definicję foo . Zauważ, że dołączenia strażników w foo.h nie zapobiegają temu, ponieważ zarówno foo.cpp i main.cpp zawierają osobno foo.h Najbardziej prawdopodobnym rezultatem próby zbudowania tego programu jest błąd czasu połączenia identyfikujący foo jako wielokrotnie zdefiniowany.

Aby uniknąć takich błędów, należy zadeklarować funkcje w nagłówkach i zdefiniować je w odpowiednich plikach .cpp , z pewnymi wyjątkami (patrz inne przykłady).

Funkcje wbudowane

Funkcja zadeklarowana jako inline może być zdefiniowana w wielu jednostkach tłumaczeniowych, pod warunkiem, że wszystkie definicje są identyczne. Musi być także zdefiniowany w każdej jednostce tłumaczeniowej, w której jest używany. Dlatego funkcje wbudowane powinny być zdefiniowane w nagłówkach i nie trzeba ich wymieniać w pliku implementacji.

Program będzie się zachowywał tak, jakby istniała jedna definicja funkcji.

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();
}

W tym przykładzie prostsza funkcja foo jest zdefiniowana w pliku nagłówkowym, podczas gdy bardziej skomplikowany bar funkcji nie jest wbudowany i jest zdefiniowany w pliku implementacji. Zarówno jednostki tłumaczące foo.cpp i main.cpp zawierają definicje foo , ale ten program jest dobrze sformułowany, ponieważ foo jest wbudowany.

Funkcja zdefiniowana w definicji klasy (która może być funkcją składową lub funkcją zaprzyjaźnioną) jest domyślnie wbudowana. Dlatego jeśli klasa jest zdefiniowana w nagłówku, funkcje składowe klasy mogą być zdefiniowane w ramach definicji klasy, nawet jeśli definicje mogą być zawarte w wielu jednostkach tłumaczeniowych:

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

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

Funkcja Foo::baz definiuje out-of-line, więc nie jest to funkcja inline, a nie muszą być zdefiniowane w nagłówku.

Naruszenie ODR przez rozwiązanie przeciążenia

Nawet przy identycznych tokenach dla funkcji wbudowanych ODR może zostać naruszone, jeśli wyszukiwanie nazw nie odnosi się do tej samej jednostki. rozważmy func w następujący sposób:

  • nagłówek. 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)`
    }
    

Mamy naruszenie ODR, ponieważ overloaded odnosi się do różnych podmiotów w zależności od jednostki tłumaczeniowej.



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow