2010-11-28 7 views
5

Je ne sais pas comment commander mes fonctions en C++. En C, j'ai simplement placé une fonction qui utilise une autre fonction au-dessous de cette fonction, aussi étroitement que possible - c'est assez commun. Comme ceci:Comment commander des fonctions en C++?

void bar() 
{ 
} 

void foo() 
{ 
    bar(); 
} 

Cependant, en C++, il existe plusieurs types de fonctions:

  • fonctions gratuites
  • Fonctions membres privées
  • Fonctions membres publiques
  • fonctions membres statiques

Je suis actuellement maki ng ma fonction pour fonction de la façon dont ils sont commandés dans le fichier .hpp, par exemple:

class Foo_bar { 
public: 
    Foo_bar(); 
    void foo(); 
private: 
    int some_member; 
    void bar(); 

Mais maintenant, si le constructeur utilise foo() ou la barre(), ceux-ci seront ci-dessous le constructeur dans la fichier source, incompatible avec ma commande habituelle. Je pourrais bien sûr réorganiser mon en-tête pour en tenir compte:

class Foo_bar { 
private: 
    int some_member; 
    void bar(); 

public: 
    void foo(); 
    Foo_bar(); 

Mais je pense que c'est un gâchis.

De plus, en Java, à l'opposé de mon premier exemple semble être commun:

void foo() 
{ 
    bar(); 
} 

void bar() 
{ 
} 

C'est probablement dû au haut vers le bas la pensée commune en POO, contrairement au bas vers le haut la pensée commune programmation procédurale/fonctionnelle. Cependant, avec des fonctions libres qui n'ont pas de prototypes, ce style descendant n'est pas possible.

Est-il même possible de commander des fonctions en C++ de manière cohérente?

+0

Je préfère commander mes fonctions par ce que je travaille - de sorte que lorsque ouvrir un fichier, il est probable que ce que je veux voir est en haut. Cela dit, j'ai beaucoup de pratique à la recherche de ":: methodName" - alors peut-être que ma méthode n'est pas la meilleure;) – sje397

+0

Je ne comprends pas la question. Toutes les fonctions ont des prototypes et vous pouvez déclarer des fonctions libres dans un fichier d'en-tête ou n'importe où ailleurs si vous en avez besoin. Tant que vous avez des déclarations visibles pour toutes les fonctions que vous utilisez, vous pouvez commander des définitions de fonction dans l'ordre que vous choisissez. Qu'est-ce qui vous empêche de commander vos fonctions d'une manière qui vous semble cohérente? –

+0

@Charles: Voir les commentaires de forneo ci-dessous. Apparemment, il pense que l'utilisation de fonctionnalités linguistiques pour ce à quoi elles étaient destinées est mauvaise ...? On dirait qu'il pose essentiellement la question hautement subjective "Je ne veux pas utiliser les fonctionnalités linguistiques disponibles, que me suggérez-vous de faire à la place?" C'est comme dire "Je n'aime vraiment pas les accolades, alors j'essaie d'ajuster 1 phrase par bloc pour les éviter, y a-t-il une manière cohérente de faire cela pour ne jamais avoir à utiliser d'accolades?" et s'attendre à ce que quelqu'un dise "Ouais! si (exp) déclaration; if (sameExp) statement2; vous obtenez 2 stats sans accolades!" Même type de Q. – Loduwijk

Répondre

6

C'est possible. Vous devez utiliser forward declaration.

Déclarez une fonction avant de la définir, et les autres fonctions la verront sans problème même si elles sont définies auparavant.

Donc, vous devriez être en mesure de le faire en C++:

void bar(); // forward declaration; note that function bar isn't defined yet 

void foo() 
{ 
    bar(); // foo knows that bar is declared, so it will search for bar's definition 
} 

void bar() // here bar is defined, so foo will use this when needed 
{ 
} 
+0

Il est techniquement possible, oui, mais IMO assez moche pour transmettre chaque fonction libre. Je ne mets pas dans un en-tête des prototypes de fonctions libres utilisées uniquement dans mon code, je fais confiance à Scott Meyers à ce sujet. http://www.drdobbs.com/184401197 – forneo

+2

@forneo: Si vous voulez dire que vous ne le mettez pas dans un fichier d'en-tête, c'est bien, Darioo n'a pas dit que vous devriez. Vous pouvez le déclarer avec un prototype en haut du même fichier que vous les utilisez. Si par en-tête vous voulez dire à l'en-tête (en haut) du même fichier, pourquoi pas? Je prototypais presque tout en haut de mes fichiers même si ce n'est pas nécessaire, et ça va; ce n'est pas terrible, c'est bien de pouvoir voir une liste des fonctions en haut. Si vous dites que vous ne ferez pas cela, alors c'est votre perte, vous dites «je ne veux pas utiliser les fonctionnalités du langage» et vous n'avez aucune question ici. – Loduwijk

+0

Je suppose que j'ai mal compris, je n'ai même pas pensé à mettre les avants dans le même fichier. Cela me permettrait de commander de haut en bas. – forneo

1

Vous déclarez la classe dans un fichier d'en-tête, non? Et mettre en œuvre la plupart de celui-ci dans un fichier séparé? Si vous implémentez simplement le constructeur dans le fichier d'implémentation et pas dans l'en-tête, je ne pense pas que vous rencontrerez le problème que vous avez mentionné (parce que l'en-tête entier sera vu avant que le constructeur appelle foo() ou bar()

+0

Oui, il n'y a pas de problème technique, je dis juste que c'est incompatible avec ma commande ascendante habituelle. – forneo

1

La commande de fonctions libres en C++ obéit aux mêmes règles que celles que vous avez mentionnées, mais comme dit Darioo, vous pouvez les déclarer et les classer comme vous le souhaitez. Les définitions dans le fichier source ne sont cependant pas possibles pour les templates, sans certaines solutions de contournement anti-template non triviales et non-générales

Dans une classe, les choses sont généralement différentes, car il n'y a quasiment aucun cas. Si vous implémentez complètement votre classe dans un en-tête, les déclarations sont toujours lues lorsque vous définissez les fonctions dans le fichier source.

Je commande habituellement des fonctions dans "fonction", et le groupe par exemple. getters et setters, constructeur/destructeur (si possible).

+0

Voir ma réponse à la réponse de darioo, je ne pense pas que toutes les fonctions doivent être déclarées dans l'en-tête, Scott Meyers fait quelques bons points. – forneo

+0

D'accord, toutes les fonctions internes spécifiques au fichier source ne doivent pas forcément figurer dans l'en-tête, si vous ne le souhaitez pas. Mais alors vous pouvez * transmettre * déclarer les fonctions dans le fichier source, et les commander en dessous de ces déclarations comme bon vous semble. – rubenvb

+0

D'accord - bien que même avec des modèles, j'aime rendre trivial le modèle réel avec le code non-template sous-jacent. C'est un peu plus de travail, mais cela permet d'éviter les ballonnements et de garder les en-têtes relativement lisibles. Bien que sur le problème de ballonnement, il est possible que les modèles sont moins sujettes aux ballonnements qu'ils ne l'étaient de toute façon. – Steve314

0

Personnellement, j'aime voir des choses qui seront référées d'ailleurs (que les gens auront besoin de trouver/lire souvent) près du haut du fichier. Les éléments internes qui, une fois stables, peuvent être oubliés, sont laissés pour plus tard.

Il y a des incohérences, cependant. Par exemple, dans une classe, cela implique de mettre les choses publiques en premier, les internes privés plus tard. Mais la visibilité par défaut pour une classe (ce que vous obtenez naturellement) est privée, et (en particulier si j'ai des méthodes en ligne), je place généralement toutes les données privées à l'avant. Il peut même s'agir d'une erreur pour une méthode de style en ligne afin de référencer une variable membre avant qu'elle ne soit définie - désolé, je souffre d'un problème de mémoire temporaire. Mais fondamentalement, l'essentiel est de rassembler des choses qui sont similaires ou logiquement liées. Une méthode begin sera adjacente à une méthode end, une méthode Step_To_Next adjacente à une méthode Step_To_Prev, etc. Le regroupement doit être similaire, les paramètres similaires, et communément utilisés ensemble sont tous bons. Ce qui appelle ce qui est surtout un détail d'implémentation, donc pas quelque chose que vous devez nécessairement souligner dans les fichiers d'en-tête que l'utilisateur de votre bibliothèque va lire - bien que dans le code d'implémentation les choses peuvent être différentes. Comme d'autres l'ont souligné, les déclarations anticipées permettent une certaine liberté.

Le plus important (1) adopte un style cohérent, et (2) ne vous inquiétez pas trop des cas ambigus.

+0

Sur le commentaire de savoir si vous pouvez ou ne pouvez pas utiliser un attribut de membre non encore déclaré à partir d'une méthode membre inline, la réponse est que vous pouvez, il est légal et commun.'struct test {void foo() {std :: cout << x << std :: endl; } int x; }; 'est le code correct. –

1

Votre préoccupation concernant la réorganisation des fonctions dans la définition de classe n'est pas correcte, comme l'indiquent les deux citations suivantes de la norme C++ 03.

9,2/2- $ « Une classe est considéré comme un type d'objet complètement défini (3.9) (ou le type complet) à la clôture} de la classe spécificateur. Au sein de l'élément de spécification classe , la classe est considérée comme complète au sein de la fonction corps, arguments par défaut et constructeur ctor-initializers (y compris des choses dans imbriquées classes). Sinon, il est considéré comme incomplète dans son propre membre spécifications classe « .

Et

3.4.1/8 $ - « Un nom utilisé dans la définition d'une fonction de membre (9.3) de classe X suivant declaratorid29 de la fonction) doivent être déclarés dans un des manières suivantes:

- avant son utilisation dans le bloc dans lequel il est utilisé ou dans un bloc englobant (6.3), ou

- est membre de la classe X ou être membre d'une classe de base de X (10.2), ou

- si X est une classe imbriquée de la classe Y (9.7), est un membre de Y, ou doit être un membre d'une classe de base de Y (cette recherche applique à son tour à des classes renfermant de Y, en commençant par la classe externe la plus intérieure ), 30) ou

- si X est une classe locale (9.8) ou est une classe imbriquée d'une classe locale, avant la définition de la classe X dans un bloc entourant la définition de la classe X, ou

- si X est un membre de l'espace de noms N ou est une classe imbriquée d'une classe qui est un membre de N, ou est un lo cal classe ou une classe imbriquée dans une classe locale d'une fonction qui est un membre de N, avant la définition de la fonction membre, dans l'espace de noms N ou dans l'un des espaces de noms de N.

En règle générale, en C++, les définitions de fonctions doivent être visibles au moment de leur utilisation. La seule exception est le cas des fonctions de membre de classe comme illustré par les citations ci-dessus. Par conséquent, cela signifie que les fonctions membres de la classe à appeler par le constructeur n'ont pas besoin d'être définies avant le constructeur lexicalement.

2

En fait, c'est une très bonne question, car la lisibilité a un impact majeur sur quiconque lira le code après vous.

Il y a 3 sortes de gens qui liront le code d'une classe:

  • ceux qui souhaitent l'utilise (et ne se soucient pas beaucoup sur ses internes)
  • ceux qui souhaitent hériter de votre classe (et ne se soucient pas beaucoup sur ses internes)
  • ceux qui souhaitent pirater votre classe, et donc vraiment soin de ses internes

pour cette raison, j'essaie de commander les en-têtes afin que nous tous er peut arrêter une fois qu'il a obtenu ce qu'il cherchait, ce qui signifie:

class Foo 
{ 
public: 
    // types 
    // static methods 
    // methods (usually constructors, 
    //   then simple accessors, 
    //   then more complicated stuff) 

protected: 
    // same pattern 

private: 
    // same pattern 
    // attributes 
}; 

// Free functions about this class 

// Implementation of inline/template methods 

Parfois je dois déclarer certains types d'avance, même si elles sont privées, mais cela est rare. Le but de cette commande est de réduire au minimum la quantité de choses qu'un lecteur doit lire avant d'obtenir ce qu'il veut (et arrête de lire et revient à ce qu'il faisait avant de devoir s'interrompre pour regarder votre code).

Ensuite, en ce qui concerne les méthodes « d'aide », cela dépend du type de code:

  • pour le code de modèle, j'utilise un espace de noms « détails », il est à la fois clair pour le lecteur qu'il ne doit pas être inquiet à propos de cela et il isoler les noms dans leur propre espace de noms afin qu'ils n'apparaissent pas dans les outils de complétion de code
  • Pour le code régulier, j'utilise un espace de noms anonyme dans le fichier source, ce qui est encore mieux car il génère des symboles invisibles et je ne court pas le risque de violer ODR.

Si un code peut exiger beaucoup d'aides, je tends à créer un fichier d'en-tête dédié dans le répertoire source, ce qui donne la structure suivante:

include/ 
    foo.hpp 

src/ 
    fooImpl.hpp --> #include "foo.hpp" 
    foo.cpp  --> #include "fooImpl.hpp" 

afin de fournir une liste des déclarations à le lecteur, car il est plus facile de parcourir une liste de déclarations que d'extraire les déclarations d'une liste de définitions, quelle que soit l'indentation et le style.

Et bien sûr, toujours pour le rendre plus facile, je commande toujours la liste des déclarations et la liste des définitions tout aussi ...

+0

Il y a aussi des gens qui souhaitent apprendre de votre code. –

+0

@druciferre: quand j'aurai atteint le point où les gens chercheront à apprendre de mon code, je serai très heureux: D –