web-dev-qa-db-fra.com

Manière correcte de définir des méthodes d'espace de noms C ++ dans un fichier .cpp

Probablement un doublon, mais pas facile à rechercher ...

Étant donné un en-tête comme:

namespace ns1
{
 class MyClass
 {
  void method();
 };
}

J'ai vu method() défini de plusieurs manières dans le fichier .cpp:

Version 1:

namespace ns1
{
 void MyClass::method()
 {
  ...
 }
}

Version 2:

using namespace ns1;

void MyClass::method()
{
 ...
}

Version 3:

void ns1::MyClass::method()
{
 ...
}

Y a-t-il une "bonne" façon de le faire? Est-ce que certains de ces "faux" en ce qu'ils ne signifient pas tous la même chose?

95
Mr. Boy

La version 2 n'est pas claire et facile à comprendre car vous ne savez pas à quel espace de nom MyClass appartient et c'est illogique (la fonction de classe ne se trouve pas dans le même espace de nom?)

La version 1 a raison car elle montre que dans l’espace de noms, vous définissez la fonction.

La version 3 est également valable car vous avez utilisé l'opérateur de résolution de portée :: Pour faire référence à MyClass::method () dans l'espace de noms ns1. Je préfère la version 3.

Voir Namespaces (C++) . C'est la meilleure façon de faire cela.

44
GILGAMESH

5 ans plus tard et je pensais mentionner ceci, qui a l'air beau et qui n'est pas mauvais

using ns1::MyClass;

void MyClass::method()
{
  // ...
}
23
Puzomor Croatia

J'utilise la version 4 (ci-dessous) car elle combine la plupart des avantages de la version 1 (résonance de la définition de résoective) et de la version 3 (maximum explicite). Le principal inconvénient est que les gens ne sont pas habitués à cela, mais comme je le considère techniquement supérieur aux alternatives, cela ne me dérange pas.

Version 4: utilisez la qualification complète en utilisant des alias d'espace de nom:

#include "my-header.hpp"
namespace OI = outer::inner;
void OI::Obj::method() {
    ...
}

Dans mon monde, j'utilise fréquemment des alias d'espaces de noms car tout est explicitement qualifié - à moins que ce ne soit pas le cas (par exemple, noms de variables) ou qu'il s'agisse d'un point de personnalisation connu (par exemple, swap () dans un modèle de fonction).

13
Dietmar Kühl

La version 3 rend l’association entre la classe et l’espace de noms très explicite aux dépens de plus de frappe. La version 1 évite cela mais capture l'association avec un bloc. La version 2 a tendance à cacher cela, donc je l'éviterais.

4
Paul Joireman

Il s'avère que ce n'est pas seulement "une question de style de codage". Num. 2 conduit à une erreur de liaison lors de la définition et de l'initialisation d'une variable déclarée extern dans le fichier d'en-tête. Regardez exemple dans ma question. Définition de la constante dans un espace de noms dans un fichier cpp

3
jakumate

Guide de style C++ de Google> dicte votre version 1, sans indentation cependant.

Googles C++ Style Guide for namespaces

3

Tous les moyens sont bons et chacun a ses avantages et ses inconvénients.

Dans la version 1, vous avez l’avantage de ne pas avoir à écrire l’espace de noms devant chaque fonction. L'inconvénient est que vous obtiendrez une identification ennuyeuse, surtout si vous avez plusieurs niveaux d'espaces de noms.

Dans la version 2, vous rendez votre code plus propre, mais si plusieurs espaces de noms sont implémentés dans le CPP, vous pouvez accéder directement aux fonctions et variables de l’autre, rendant ainsi votre espace de noms inutile (pour le fichier cpp).

Dans la version 3, vous devrez taper plus et vos lignes de fonction peuvent être plus grandes que l'écran, ce qui est mauvais pour les effets de conception.

Il y a aussi une autre façon dont certaines personnes l'utilisent. C'est similaire à la première version, mais sans les problèmes d'identification.

C'est comme ça:

#define OPEN_NS1 namespace ns1 { 
#define CLOSE_NS1 }

OPEN_NS1

void MyClass::method()
{
...
}

CLOSE_NS1

A vous de choisir lequel est le mieux adapté à chaque situation =]

2
Renan Greinert

Je choisis Num.3 (a.k.a. la version commentée). C'est plus typé, mais l'intention est exacte pour vous et pour le compilateur. Le problème que vous avez posté tel quel est en réalité plus simple que le monde réel. Dans le monde réel, il existe d'autres champs d'application pour les définitions, pas seulement les membres de la classe. Vos définitions ne sont pas très compliquées avec les classes uniquement, car leur portée n'est jamais rouverte (contrairement aux espaces de noms, à la portée globale, etc.).

Num.1, cela peut échouer avec des portées autres que les classes - tout ce qui peut être rouvert. Donc, vous pouvez déclarer une nouvelle fonction dans un espace de noms en utilisant cette approche, ou vos inlines pourraient finir par être substitués via ODR. Vous en aurez besoin pour certaines définitions (notamment les spécialisations de modèles).

Num.2 Ceci est très fragile, en particulier dans les bases de code volumineuses - à mesure que les en-têtes et les dépendances changent, la compilation de votre programme échouera.

Num.3 C'est idéal, mais il y a beaucoup de choses à taper - ce que vous voulez définir quelque chose. Cela fait exactement cela, et le compilateur se met en marche pour s’assurer que vous n’avez pas commis d’erreur, une définition n’est pas synchronisée avec sa déclaration, etc.

2
justin