Quelles sont les différences entre les définitions des paramètres comme (type et nom), et (type * nom)?

StackOverflow https://stackoverflow.com/questions/948947

  •  09-09-2019
  •  | 
  •  

Question

Une question très basique, mais encore, il serait bon d'entendre de C ++ gourous là-bas.

Il y a deux façons assez similaires à déclarer des paramètres par référence en C ++.

1) En utilisant "astérisque":

void DoOne(std::wstring* iData);

2) A l'aide "esperluette":

void DoTwo(std::wstring& iData);

Quelles sont les implications de chaque méthode? Y a-t-il des années Gotcha dans tous les cas?

Bonus # 1: Quelle serait une façon formelle à appeler la méthode à # 1 et # 2? Sont-ils tous les deux appelés « par référence »?

Bonus # 2: std :: wstring est utilisé délibérément. Quelles seraient les implications vers les classes de la bibliothèque standard dans chaque cas?

Était-ce utile?

La solution

n ° 1 utilise un paramètre de pointeur ( « faire passer un pointeur vers »), n ° 2 utilise un paramètre de référence ( «de passage par référence). Ils sont très semblables, mais notez que le code d'appel est différent dans les deux cas:

std::wstring s;

DoOne(&s); // pass a pointer to s
DoTwo(s); // pass s by reference

Certaines personnes préfèrent # 1, en utilisant une convention qui passe par le pointeur indique que la fonction peut changer la valeur de s (même si la fonction soit possible). D'autres personnes (y compris moi) préfèrent # 2, puisque le passage par référence ne permet pas NULL à passer.

Il y a une autre différence importante lors du passage par le pointeur de const ou de référence. Une variable temporaire ne peut être transmis à un paramètre de référence const:

void ByConstPointer(const std::wstring&);
void ByConstReference(const std::wstring*);

void test()
{
  ByConstPointer(&std::wstring(L"Hello")); // error: cannot take address of temporary
  ByConstReference(std::wstring(L"Hello")); // fine
}

Autres conseils

Règle numéro un pour ceci: Si NULL est une valeur valide pour le paramètre de fonction dans le contexte de la fonction, puis passer comme pointeur, sinon il passe comme référence

.

Justification, si elle ne peut pas (ne devrait pas!) Jamais NULL, alors ne vous mettez pas par la peine de vérifier la valeur NULL.

Tout en écrivant des exemples, je suis venu avec ma propre réponse. Autre chose que ci-dessous?

Le résultat de chacune d'entre elles est très similaire: une référence à un objet dans la mémoire se termine au sein de la portée du procédé. Il semble y avoir aucune exigence de mémoire stricte pour l'un d'eux. L'objet peut être soit sur la pile ou en tas.

Dans le cas de la pile chacune des méthodes seraient appelés comme ceci:

{
    std::wstring data;
    DoOne(&data);
    DoTwo(data);
}

Et pourtant, en ce qui concerne le tas, la deuxième approche, il faudrait que l'objet doit exister avant d'appeler la méthode. Si l'objet n'existe pas, l'appelant causerait exception et non la callee.

{
    std::wstring* pData = new std::wstring();
    DoOne(pData);
    DoTwo(*pData);
}

Dans ce qui précède, si hors de la mémoire condition se produit et pData finit par NULL, l'accident se passerait-il avant DoTwo, mais DOONE avalerait NULL et peut se bloquer quelque temps plus tard.

Je ne reviendrai pas moi-même appelle un gurer C ++ (sauf sur mon CV), mais je dirais; à moins que theres toute utilisation de faire passer le paramètre comme un pointeur (à savoir la fonction veut vérifier null), toujours utiliser une référence.

Cela vaut également pour les fonctions des objets retournant, retournant un pointeur est dit en quelque sorte que l'utilisateur de la classe qu'il pourrait être nulle.

Dans DOONE, iData peut être attribué NULL. Si vous utilisez ce après avoir appelé DOONE, l'application se bloque.

Quelque chose comme

void DoOne(std::wstring* iData)
{
   //Use iData
   delete iData;
   iData = NULL;
}

et

{
    std::wstring* pData = new std::wstring();
    DoOne(pData);
    pData->someFunction(); //Crash
}

Votre réponse est tout à fait tort quand vous dites:

  

Le résultat de chacun d'entre eux est tout à fait   similaire: une référence à un objet dans   les extrémités de la mémoire au sein du procédé   portée. Il semble y avoir aucune stricte   besoins en mémoire pour l'un d'eux.

connsider:

void f( int * p1 ) {
   int ** p2 = & p1;
}

ici p1 a une « exigence de mémoire » précise - il doit exister et je dois être en mesure de prendre son adresse. Cela contraste avec

void f( int & r ) }
   int * p = & r;
}

r ici n'a pas d'existence propre, il est simplement une référence. Whem Je prends son adresse que je prends l'adresse de la chose qui fait référence à r.

Vos remarques concernant le pointeur NULL sont également erreur. Derefrencing le pointeur NULL provoque un comportement non défini - ce qui peut ou ne peut pas donner lieu à un accident.

Si vous écrivez une fonction qui obtient une variable par pointeur, vous aurez très probablement, pour vérifier si le pointeur est valide (par exemple, NULL), sinon vous risquez de plantage du programme.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top