Question

Là où je travaille, les gens pensent généralement qu’il est préférable d’initialiser les objets avec une construction de style C ++ (avec des parenthèses), alors que les types primitifs devraient être initialisés avec l’opérateur =:

std::string strFoo( "Foo" );
int nBar = 5;

Personne ne semble pouvoir expliquer pourquoi il préfère les choses de cette façon. Je peux voir que std::string = "Foo"; serait inefficace, car cela impliquerait une copie supplémentaire, mais qu’est-ce qui ne va pas avec simplement bannir l’opérateur = et utiliser des parenthèses partout?

Est-ce une convention commune? Quelle est la pensée derrière cela?

Était-ce utile?

La solution

Sauf si vous avez prouvé l'importance des performances, je ne m'inquiéterais pas d'une copie supplémentaire à l'aide de l'opérateur d'attribution de votre exemple (std::string foo = "Foo";). Je serais assez surpris que cette copie existe même une fois que vous avez examiné le code optimisé. Je pense que cela appellera le constructeur paramétré approprié.

En réponse à votre question, oui, je dirais que c'est une convention assez commune. En règle générale, les utilisateurs ont utilisé l'assignation pour initialiser les types intégrés, et il n'y a aucune raison impérieuse de modifier la tradition. La lisibilité et les habitudes sont des raisons parfaitement valables pour cette convention, compte tenu de son faible impact sur le code final.

Autres conseils

Initialiser des variables avec l'opérateur = ou avec un appel de constructeur est sémantiquement identique, c'est juste une question de style. Je préfère l'opérateur =, car il lit plus naturellement.

L'utilisation de l'opérateur = habituellement ne génère pas de copie supplémentaire, il appelle simplement le constructeur normal. Notez cependant qu'avec les types non primitifs, il ne s'agit que d'initialisations effectuées en même temps que les déclarations. Comparer:

std::string strFooA("Foo");  // Calls std::string(const char*) constructor
std::string strFoo = "Foo";  // Calls std::string(const char*) constructor
                             // This is a valid (and standard) compiler optimization.

std::string strFoo;  // Calls std::string() default constructor
strFoo = "Foo";      // Calls std::string::operator = (const char*)

Lorsque vous avez des constructeurs par défaut non triviaux, cette dernière construction peut s'avérer légèrement plus inefficace.

La norme C ++ , section 8.5 Le paragraphe 14 dit:

  

Sinon (c'est-à-dire, pour les cas d'initialisation de copie restants), un temporaire est créé. Les séquences de conversion définies par l'utilisateur qui peuvent convertir du type source en type destination ou une classe dérivée de celles-ci sont énumérées (13.3.1.4) et la meilleure est choisie par résolution de surcharge (13.3). La conversion définie par l'utilisateur ainsi sélectionnée est appelée pour convertir l'expression d'initialisation en une variable temporaire, dont le type est le type renvoyé par l'appel de la fonction de conversion définie par l'utilisateur, avec les qualificatifs cv.   du type de destination. Si la conversion est impossible ou ambiguë, l’initialisation est mal formée. L'objet en cours d'initialisation est alors directement initialisé   à partir du temporaire selon les règles ci-dessus. 87 ) Dans certains cas, une implémentation est autorisée pour éliminer le temporaire en initialisant directement l'objet; voir 12.2.

Une partie de la section 12.2 indique:

  

Même lorsque la création de l'objet temporaire est évitée, toutes les restrictions sémantiques doivent être respectées comme si l'objet temporaire était créé. [Exemple:   même si le constructeur de copie n'est pas appelé, toutes les restrictions sémantiques, telles que l'accessibilité (11), doivent être satisfaites. ]

Je viens juste de ressentir le besoin d'un autre post bête et stupide.

string str1 = "foo";

est appelé initialisation de la copie , car ce que le compilateur fait, s'il n'élimine aucun délai, est:

string str1(string("foo")); 

à côté de vérifier que le constructeur de conversion utilisé est implicite. En fait, toutes les conversions implicites sont définies par la norme en termes d’initialisation de la copie. On dit qu'une conversion implicite du type U en type T est valide si

T t = u; // u of type U

est valide.

En revanche,

string str1("foo");

fait exactement ce qui est écrit et s'appelle initialisation directe . Cela fonctionne aussi avec des constructeurs explicites.

Au fait, vous pouvez désactiver la suppression des temporaires en utilisant -fno-elide-constructors:

-fno-elide-constructors
    The C++ standard allows an implementation to omit creating a temporary which 
    is only used to initialize another object of the same type. Specifying this 
    option disables that optimization, and forces G++ to call the copy constructor 
    in all cases.

La norme indique qu'il n'y a pratiquement aucune différence entre

T a = u;

et

T a(u);

si T et le type de u sont des types primitifs. Donc, vous pouvez utiliser les deux formes. Je pense que c'est juste le style qui fait que les gens utilisent la première forme plutôt que la seconde.

Certaines personnes peuvent utiliser le premier dans certaines situations, car elles souhaitent lever la ambiguïté de la déclaration:

T u(v(a));

peut regarder quelqu'un comme la définition d'une variable u qui est initialisée à l'aide d'un temporaire d'un type v qui obtient un paramètre pour son constructeur appelé a. Mais en fait, voici ce que fait le compilateur:

T u(v a);

Il crée une déclaration de fonction qui prend un argument de type <=> et avec un paramètre appelé <=>. Donc, les gens font

T u = v(a);

pour lever l’ambiguïté, même s’ils auraient pu le faire

T u((v(a)));

aussi, car il n'y a jamais de parenthèses autour des paramètres de fonction, le compilateur le lirait comme une définition de variable plutôt que comme une déclaration de fonction:)

Vous trouverez probablement ce code tel que

std::string strFoo = "Foo";

évitera de faire une copie supplémentaire et compilera le même code (appel d'un constructeur à argument unique) que celui avec des parenthèses.

D’autre part, il existe des cas où un doit utiliser des parenthèses, comme une liste d’initialisation des membres du constructeur.

Je pense que l'utilisation de = ou de parenthèses pour construire des variables locales est en grande partie une question de choix personnel.

Eh bien, qui sait ce qu'ils pensent , mais je préfère également le = pour les types primitifs, principalement parce qu'ils ne sont pas des objets et que c'est & "d'habitude &"; manière de les initialiser.

C'est une question de style. Même la déclaration que & "; Std :: string = &"; Foo & ";; serait inefficace car cela impliquerait une copie supplémentaire " n'est pas correcte. Cette & Quot; copie supplémentaire & Quot; est supprimé par le compilateur.

Je pense que c’est plus une habitude, très peu d’objets peuvent être initialisés avec =, la chaîne en fait partie. C’est aussi une façon de faire ce que vous avez dit & "En utilisant la parenthèse partout (que le langage vous permet de l’utiliser") & ";

Un argument que l'on pourrait faire pour:

std :: string foo (" bar ");

Est-ce que cela garde les mêmes choses même si le nombre d'arguments change, c'est-à-dire:

std :: string foo (" barre " ;, 5);

Ne fonctionne pas avec le signe '='.

Un autre problème est que, pour de nombreux objets, un '=' ne semble pas naturel, par exemple, vous avez une classe Array où l'argument donne la longueur:

Tableau arr = 5;

Ne se sent pas bien, car on ne construit pas un tableau avec la valeur 5, mais avec la longueur 5:

Tableau arr (5);

semble plus naturel, car vous construisez un objet avec le paramètre donné, pas seulement en copiant une valeur.

Mais pour vous dérouter encore plus, vous initialisez les primitives dans la liste d’initialisation à l’aide de la syntaxe de l’objet.

foo::foo()   
  ,anInt(0)   
  ,aFloat(0.0)   
{   
}   
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top