Recherche…


Multiplier la fonction définie

La conséquence la plus importante de la règle de définition unique est que les fonctions non intégrées avec un lien externe ne doivent être définies qu'une seule fois dans un programme, bien qu'elles puissent être déclarées plusieurs fois. Par conséquent, ces fonctions ne doivent pas être définies dans les en-têtes, car un en-tête peut être inclus plusieurs fois à partir de différentes unités de traduction.

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

Dans ce programme, la fonction foo est définie dans l'en-tête foo.h , qui est incluse deux fois: une fois depuis foo.cpp et une fois depuis main.cpp . Chaque unité de traduction contient donc sa propre définition de foo . Notez que les gardes d' foo.h dans foo.h n'empêchent pas que cela se produise, puisque foo.cpp et main.cpp chacun séparément foo.h Le résultat le plus probable de la tentative de création de ce programme est une erreur de lien-time identifiant foo comme ayant été définie par une multiplication.

Pour éviter de telles erreurs, il convient de déclarer les fonctions dans les en-têtes et de les définir dans les fichiers .cpp correspondants, à quelques exceptions près (voir d'autres exemples).

Fonctions en ligne

Une fonction déclarée en inline peut être définie dans plusieurs unités de traduction, à condition que toutes les définitions soient identiques. Il doit également être défini dans chaque unité de traduction dans laquelle il est utilisé. Par conséquent, les fonctions en ligne doivent être définies dans les en-têtes et il n'est pas nécessaire de les mentionner dans le fichier d'implémentation.

Le programme se comportera comme s'il y avait une seule définition de la fonction.

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

Dans cet exemple, la fonction plus simple foo est définie en ligne dans le fichier d'en-tête alors que la bar fonctions plus compliquée n'est pas intégrée et est définie dans le fichier d'implémentation. Les deux unités de traduction foo.cpp et main.cpp contiennent des définitions de foo , mais ce programme est bien formé puisque foo est en ligne.

Une fonction définie dans une définition de classe (qui peut être une fonction membre ou une fonction amie) est implicitement intégrée. Par conséquent, si une classe est définie dans un en-tête, les fonctions membres de la classe peuvent être définies dans la définition de classe, même si les définitions peuvent être incluses dans plusieurs unités de traduction:

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

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

La fonction Foo::baz est définie hors ligne, ce n'est donc pas une fonction inline et ne doit pas être définie dans l'en-tête.

Violation d'ODR via une résolution de surcharge

Même avec des jetons identiques pour les fonctions en ligne, les ODR peuvent être violés si la recherche de noms ne fait pas référence à la même entité. considérons func en suivant:

  • 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)`
    }
    

Nous avons une violation de l'ODR comme overloaded fait référence à différentes entités en fonction de l'unité de traduction.



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow