Question

Je suis nouveau à la programmation C ++, mais j'ai l'expérience en Java. Je besoin de conseils sur la façon de passer des objets à des fonctions en C ++.

Dois-je passer des pointeurs, des références, ou non-pointeur et les valeurs non-référence? Je me souviens en Java, il n'y a pas ces problèmes puisque nous passons simplement la variable qui tient référence aux objets.

Il serait génial si vous pouviez aussi expliquer où utiliser chacune de ces options.

Était-ce utile?

La solution

Règles générales pour C ++ 11:

Passe par la valeur , sauf si

  1. vous n'avez pas besoin propriété de l'objet et un simple alias fera, dans ce cas, vous passer par référence const ,
  2. vous devez muter l'objet, dans ce cas, utilisez passer par une référence lvalue non-const ,
  3. vous passez des objets de classes dérivées comme classes de base, auquel cas vous devez passer par référence . (Utilisez les règles précédentes pour déterminer si le passage par référence const ou non.)

Passing par pointeur est pratiquement jamais conseillé. Les paramètres facultatifs sont mieux exprimés en std::optional (boost::optional pour libs std plus), et l'aliasing se fait très bien par référence.

11 de la sémantique de mouvement C font de passage et le retour en valeur beaucoup plus attrayante, même pour des objets complexes.


Règles générales pour C ++ 03:

par référence const , sauf si

  1. ils doivent être modifiés dans la fonction et ces changements doivent se refléter à l'extérieur, auquel cas vous passer par référence non const
  2. la fonction doit être appelable sans aucun argument, auquel cas vous passez par le pointeur, de sorte que les utilisateurs peuvent passer NULL / 0 / nullptr à la place; appliquer la règle précédente pour déterminer si vous devez passer par un pointeur sur un argument const
  3. ils sont des types intégrés, qui peut être adoptée par copie
  4. ils doivent être modifiés dans la fonction et ces changements devraient pas se refléter à l'extérieur, dans ce cas, vous pouvez passer par copie (une alternative serait de passer selon les règles précédentes et faire une copie à l'intérieur de la fonction)

(ici, « passer par la valeur » est appelée « passe par copie », car en passant par la valeur crée toujours une copie en C ++ 03)


Il y a plus à cela, mais ces quelques règles de débutants vous obtiendrez assez loin.

Autres conseils

Il y a quelques différences dans les conventions d'appel en C ++ et Java. En C ++ il y a techniquement parlant seulement deux conventions: un passage par valeur et passer par référence, avec une certaine littérature, y compris une troisième convention passe par pointeur (qui est passée par valeur réellement d'un type de pointeur). En plus de cela, vous pouvez ajouter const-ness du type de l'argument, l'amélioration de la sémantique.

passer par référence

Passing par référence signifie que la fonction recevra conceptuellement votre instance d'objet et non une copie de celui-ci. La référence est conceptuellement un alias à l'objet qui a été utilisé dans le contexte d'appel, et ne peut être nulle. Toutes les opérations effectuées à l'intérieur de la fonction appliquer à l'objet en dehors de la fonction. Cette convention n'est pas disponible en Java ou C.

passage par valeur (et le passage par pointeur)

Le compilateur génère une copie de l'objet dans le contexte d'appel et d'utiliser cette copie dans la fonction. Toutes les opérations effectuées à l'intérieur de la fonction sont faites à la copie, pas l'élément externe. Ceci est la convention pour les types primitifs en Java.

Une version particulière de celui-ci passe un pointeur (adresse de l'objet) dans une fonction. La fonction reçoit le pointeur, et toutes opérations appliquée à l'aiguille elle-même sont appliquées à la copie (pointeur), d'autre part, les opérations appliquées au pointeur déréférencé sera applicable à l'instance d'objet à cet emplacement de mémoire, de sorte que la fonction peuvent avoir des effets secondaires. L'effet de l'utilisation passe par la valeur d'un pointeur vers l'objet permettra à la fonction interne pour modifier les valeurs externes, avec passage par référence et permettra également de valeurs facultatives (passer un pointeur null).

Ceci est la convention utilisée dans C lorsqu'une fonction a besoin de modifier une variable externe et la convention utilisée en Java avec des types de référence: la référence est copiée, mais l'objet visé est le même: des modifications à la référence / pointeur sont pas visible en dehors de la fonction, mais les changements à la mémoire pointue sont.

Ajout const à l'équation

En C ++, vous pouvez assigner-ness constante à des objets lors de la définition des variables, des pointeurs et des références à différents niveaux. Vous pouvez déclarer une variable constante, vous pouvez déclarer une référence à une instance constante, et vous pouvez définir tous les pointeurs vers des objets constants, des pointeurs constants à des objets mutables et pointeurs constants à des éléments constants. A l'inverse en Java, vous ne pouvez définir un niveau de-ness constante (mot-clé final): celle de la variable (par exemple pour les types primitifs, référence pour les types de référence), mais vous ne pouvez pas définir une référence à un élément immuable (à moins que la classe elle-même est immuable).

Ceci est largement utilisé en C ++ les conventions d'appel. Lorsque les objets sont de petite taille, vous pouvez passer l'objet en valeur. Le compilateur génère une copie, mais cette copie n'est pas une opération coûteuse. Pour tout autre type, si la fonction ne changera pas l'objet, vous pouvez passer une référence à une instance constante (généralement appelée référence constante) du type. Ce ne sera pas copier l'objet, mais passer dans la fonction. Mais en même temps, le compilateur garantit que l'objet ne change pas dans la fonction.

Règles générales

Ce sont quelques règles de base à suivre:

  • Préfère valeur passe par pour les types primitifs
  • Préférez pour d'autres types passe par référence avec des références à la constante
  • Si la fonction doit modifier le passage par référence à l'utilisation de l'argument
  • Si l'argument est facultatif, l'utilisation passe par pointeur (à constante si la valeur facultative ne doit pas être modifiée)

Il y a d'autres petits écarts par rapport à ces règles, dont la première est la manipulation la propriété d'un objet. Lorsqu'un objet est allouée dynamiquement avec le nouveau, il doit être désallouée avec suppression (ou les versions [] de ceux-ci). L'objet ou function qui est responsable de la destruction de l'objet est considéré comme le propriétaire de la ressource. Lors de la création d'un objet alloué dynamiquement dans un morceau de code, mais la propriété est transférée à un élément différent, il est généralement fait avec la sémantique passe-par-pointeur, ou si possible avec des pointeurs intelligents.

Side note

Il est important d'insister sur l'importance de la différence entre C ++ et Java références. Dans les références C sont conceptuellement de l'instance de l'objet, et non pas un accesseur à elle. L'exemple le plus simple est implémenter une fonction d'échange:

// C++
class Type; // defined somewhere before, with the appropriate operations
void swap( Type & a, Type & b ) {
   Type tmp = a;
   a = b;
   b = tmp;
}
int main() {
   Type a, b;
   Type old_a = a, old_b = b;
   swap( a, b );
   assert( a == old_b );
   assert( b == old_a ); 
}

La fonction d'échange ci-dessus change à la fois ses arguments grâce à l'utilisation de références. Le code le plus proche en Java:

public class C {
   // ...
   public static void swap( C a, C b ) {
      C tmp = a;
      a = b;
      b = tmp;
   }
   public static void main( String args[] ) {
      C a = new C();
      C b = new C();
      C old_a = a;
      C old_b = b;
      swap( a, b ); 
      // a and b remain unchanged a==old_a, and b==old_b
   }
}

La version Java du code modifiera les copies des références en interne, mais ne modifiera pas les objets réels à l'extérieur. références de Java sont des pointeurs C sans l'arithmétique des pointeurs qui sont transmis par la valeur en fonctions.

Il y a plusieurs cas à considérer.

paramètre modifié ( "out" et les paramètres "in / out")

void modifies(T &param);
// vs
void modifies(T *param);

Cette affaire est la plupart du temps sur le style: voulez-vous le code comme appel (obj) ou appel (& obj) ? Cependant, il y a deux points où la différence importe: le cas en option, ci-dessous, et vous souhaitez utiliser une référence en cas de surcharge des opérateurs

.

... et en option

void modifies(T *param=0);  // default value optional, too
// vs
void modifies();
void modifies(T &param);

Paramètre non modifiés

void uses(T const &param);
// vs
void uses(T param);

Ceci est le cas intéressant. La règle de base est « pas cher pour copier » types sont passés par valeur - ce sont généralement de petits types (mais pas toujours) - tandis que d'autres sont passés par ref const. Toutefois, si vous avez besoin de faire une copie de votre fonction quel que soit, vous devrait passer par la valeur . (Oui, ce qui expose un peu de détails de mise en œuvre. C'est le C ++. )

... et en option

void uses(T const *param=0);  // default value optional, too
// vs
void uses();
void uses(T const &param);  // or optional(T param)

Il y a la moindre différence ici entre toutes les situations, afin de choisir celui qui rend votre vie plus facile.

Const en valeur est un détail de mise en œuvre

void f(T);
void f(T const);

Ces déclarations sont en fait la même fonction exacte! Lors du passage par valeur, const est purement un détail de mise en œuvre. Essayez-:

void f(int);
void f(int const) { /* implements above function, not an overload */ }

typedef void NC(int);       // typedefing function types
typedef void C(int const);

NC *nc = &f;  // nc is a function pointer
C *c = nc;    // C and NC are identical types

Passe en valeur:

void func (vector v)

variables de passage par valeur lorsque la fonction a besoin une isolation complète de l'environnement à-dire pour empêcher la fonction de la modification de la variable d'origine ainsi que pour empêcher d'autres fils de modifier sa valeur lorsque la fonction est exécutée.

L'inconvénient est les cycles de CPU et de la mémoire supplémentaire dépensé pour copier l'objet.

Passe par référence const:

void func (const vector& v);

Cette forme émule le comportement passe par valeur, tout en éliminant la surcharge de la copie. La fonction obtient l'accès en lecture à l'objet original, mais ne peut pas modifier sa valeur.

L'inconvénient est la sécurité de fil: toute modification apportée à l'objet original par un autre thread sera affiché dans la fonction pendant qu'il est encore l'exécution

.

passer par référence non-const:

void func (vector& v)

Utilisez cette fonction lorsque la fonction doit écrire de nouveau une valeur à la variable, qui finalement s'utilisé par l'appelant.

Tout comme le cas de référence const, ce n'est pas thread-safe.

Passe par pointeur const:

void func (const vector* vp);

Fonctionnellement même que passe par const référence à l'exception de la syntaxe différente, plus le fait que la fonction d'appel peut passer le pointeur NULL pour indiquer qu'il n'a pas de données valides pour passer.

Non thread-safe.

Passe par pointeur non-const:

void func (vector* vp);

Tout comme référence non const. L'appelant fixent une variable à NULL lorsque la fonction est pas censé écrire de nouveau une valeur. Cette convention est considérée dans de nombreuses API glibc. Exemple:

void func (string* str, /* ... */) {
    if (str != NULL) {
        *str = some_value; // assign to *str only if it's non-null
    }
}

Comme tout passe par référence / pointeur, pas thread-safe.

Puisque personne ne dit que je suis d'ajouter là-dessus, quand vous passez un objet à une fonction en c ++ le constructeur de copie par défaut de l'objet est appelé si vous n'avez pas un qui crée un clone de l'objet, puis le transmettre à la méthode, lorsque vous modifiez les valeurs d'objet qui refléteront sur la copie de l'objet au lieu de l'objet original, qui est le problème en c ++, donc si vous faites tous les attributs de classe pour être pointeurs, alors les constructeurs de copie copieront les adresses des attributs de pointeur, donc quand les invocations de méthode sur l'objet qui manipule les valeurs stockées dans le pointeur attribue les adresses, les modifications reflètent également dans l'objet d'origine qui est passé comme paramètre, donc cela peut se comporter même Java mais n'oubliez pas que tous vos attributs de classe doivent être pointeurs, vous devez également modifier les valeurs des pointeurs, sera beaucoup plus clair avec l'explication du code.

Class CPlusPlusJavaFunctionality {
    public:
       CPlusPlusJavaFunctionality(){
         attribute = new int;
         *attribute = value;
       }

       void setValue(int value){
           *attribute = value;
       }

       void getValue(){
          return *attribute;
       }

       ~ CPlusPlusJavaFuncitonality(){
          delete(attribute);
       }

    private:
       int *attribute;
}

void changeObjectAttribute(CPlusPlusJavaFunctionality obj, int value){
   int* prt = obj.attribute;
   *ptr = value;
}

int main(){

   CPlusPlusJavaFunctionality obj;

   obj.setValue(10);

   cout<< obj.getValue();  //output: 10

   changeObjectAttribute(obj, 15);

   cout<< obj.getValue();  //output: 15
}

Mais ce n'est pas une bonne idée que vous conclurez par écrire beaucoup de code impliquant des pointeurs, qui sont sujettes à des fuites de mémoire et ne pas oublier d'appeler Destructeurs. Et pour éviter ce c ++ ont un constructeur de copie où vous allez créer une nouvelle mémoire lorsque les objets contenant des pointeurs sont passés à la fonction des arguments qui arrêteront la manipulation d'autres données d'objets, Java ne passe par la valeur et la valeur est référence, donc il ne nécessite pas un constructeur de copie.

Il existe trois méthodes consistant à faire passer un objet à une fonction en tant que paramètre:

  1. passer par référence
  2. passe par la valeur
  3. l'ajout constant dans le paramètre

Passez par l'exemple suivant:

class Sample
{
public:
    int *ptr;
    int mVar;

    Sample(int i)
    {
        mVar = 4;
        ptr = new int(i);
    }

    ~Sample()
    {
        delete ptr;
    }

    void PrintVal()
    {
        cout << "The value of the pointer is " << *ptr << endl
             << "The value of the variable is " << mVar;
   }
};

void SomeFunc(Sample x)
{
cout << "Say i am in someFunc " << endl;
}


int main()
{

  Sample s1= 10;
  SomeFunc(s1);
  s1.PrintVal();
  char ch;
  cin >> ch;
}

Sortie:

  

Dire que je suis en someFunc
  La valeur du pointeur est -17.891.602
  La valeur de la variable est 4

Voici les façons de passer un arguments / paramètres pour fonctionner en C ++.

1. par valeur.

// passing parameters by value . . .

void foo(int x) 
{
    x = 6;  
}

2. par référence.

// passing parameters by reference . . .

void foo(const int &x) // x is a const reference
{
    x = 6;  
}

// passing parameters by const reference . . .

void foo(const int &x) // x is a const reference
{
    x = 6;  // compile error: a const reference cannot have its value changed!
}

3. par objet.

class abc
{
    display()
    {
        cout<<"Class abc";
    }
}


// pass object by value
void show(abc S)
{
    cout<<S.display();
}

// pass object by reference
void show(abc& S)
{
    cout<<S.display();
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top