44 votes

Comment retourner un objet de classe par référence en C++ ?

J'ai une classe appelée Object qui stocke certaines données.

Je voudrais le renvoyer par référence en utilisant une fonction comme celle-ci :

    Object& return_Object();

Ensuite, dans mon code, je l'appellerais comme ceci :

    Object myObject = return_Object();

J'ai écrit un code comme celui-ci et il se compile. Cependant, lorsque j'exécute le code, j'obtiens systématiquement un seg fault. Quelle est la bonne façon de renvoyer un objet de classe par référence ?

63voto

Chowlett Points 19889

Vous retournez probablement un objet qui est sur la pile. C'est-à-dire, return_Object() ressemble probablement à ça :

Object& return_Object()
{
    Object object_to_return;
    // ... do stuff ...

    return object_to_return;
}

Si c'est ce que vous faites, vous n'avez pas de chance - object_to_return est sortie du champ d'application et a été détruite à la fin de l'opération. return_Object Así que myObject fait référence à un objet inexistant. Vous devez soit retourner par valeur, soit retourner un objet Object déclarée dans un champ d'application plus large ou new sur le tas.

24voto

UmNyobe Points 9508

Vous pouvez uniquement utiliser

     Object& return_Object();

si l'objet renvoyé a une portée supérieure à celle de la fonction. Par exemple, vous pouvez l'utiliser si vous avez une classe où il est encapsulé. Si vous créez un objet dans votre fonction, utilisez des pointeurs. Si vous voulez modifier un objet existant, passez-le comme argument.

  class  MyClass{
      private:
        Object myObj;

      public:
         Object& return_Object() {
            return myObj;
         }

         Object* return_created_Object() {
            return new Object();
         }

         bool modify_Object( Object& obj) {
            //  obj = myObj; return true; both possible
            return obj.modifySomething() == true;
         }
   };

16voto

spraff Points 10492

Vous ne pouvez renvoyer les objets non locaux que par référence. Le destructeur peut avoir invalidé un pointeur interne, ou autre chose.

N'ayez pas peur de renvoyer des valeurs -- c'est rapide !

9voto

Jayhello Points 703

Je vais vous montrer quelques exemples :

Premier exemple, ne pas renvoyer d'objet de portée locale, par exemple :

const string &dontDoThis(const string &s)
{
    string local = s;
    return local;
}

Vous ne pouvez pas retourner local par référence, car local est détruit à la fin du corps de dontDoThis .

Deuxième exemple, vous pouvez renvoyer par référence :

const string &shorterString(const string &s1, const string &s2)
{
    return (s1.size() < s2.size()) ? s1 : s2;
}

Ici, vous pouvez renvoyer par référence les deux s1 y s2 car ils ont été définis avant shorterString a été appelé.

Troisième exemple :

char &get_val(string &str, string::size_type ix)
{
    return str[ix];
}

code d'utilisation comme ci-dessous :

string s("123456");
cout << s << endl;
char &ch = get_val(s, 0); 
ch = 'A';
cout << s << endl; // A23456

get_val peut retourner des éléments de s par référence parce que s existe toujours après l'appel.

Quatrième exemple

class Student
{
public:
    string m_name;
    int age;    

    string &getName();
};

string &Student::getName()
{
    // you can return by reference
    return m_name;
}

string& Test(Student &student)
{
    // we can return `m_name` by reference here because `student` still exists after the call
    return stu.m_name;
}

exemple d'utilisation :

Student student;
student.m_name = 'jack';
string name = student.getName();
// or
string name2 = Test(student);

Cinquième exemple :

class String
{
private:
    char *str_;

public:
    String &operator=(const String &str);
};

String &String::operator=(const String &str)
{
    if (this == &str)
    {
        return *this;
    }
    delete [] str_;
    int length = strlen(str.str_);
    str_ = new char[length + 1];
    strcpy(str_, str.str_);
    return *this;
}

Vous pourriez alors utiliser le operator= au-dessus comme ça :

String a;
String b;
String c = b = a;

2voto

Peter Horvath Points 2177

Bon, ce n'est peut-être pas une très belle solution dans le code, mais c'est très beau dans l'interface de votre fonction. Et elle est également très efficace. C'est l'idéal si le deuxième aspect est plus important pour vous (par exemple, si vous développez une bibliothèque).

L'astuce est la suivante :

  1. Une ligne A a = b.make(); est converti en interne en un constructeur de A, c'est-à-dire comme si vous aviez écrit A a(b.make()); .
  2. Maintenant b.make() devrait donner lieu à une nouvelle classe, avec une fonction de rappel.
  3. Tout cela ne peut être géré que par des classes, sans aucun modèle.

Voici mon exemple minimal. Vérifiez seulement le main() Comme vous pouvez le constater, c'est simple. Les internes ne le sont pas.

Du point de vue de la vitesse : la taille d'une Factory::Mediator est seulement de 2 pointeurs, ce qui est plus qu'un mais pas plus. Et c'est le seul objet de l'ensemble qui est transféré par valeur.

#include <stdio.h>

class Factory {
  public:
    class Mediator;

    class Result {
      public:
        Result() {
          printf ("Factory::Result::Result()\n");
        };

        Result(Mediator fm) {
          printf ("Factory::Result::Result(Mediator)\n");
          fm.call(this);
        };
    };

    typedef void (*MakeMethod)(Factory* factory, Result* result);

    class Mediator {
      private:
        Factory* factory;
        MakeMethod makeMethod;

      public:
        Mediator(Factory* factory, MakeMethod makeMethod) {
          printf ("Factory::Mediator::Mediator(Factory*, MakeMethod)\n");
          this->factory = factory;
          this->makeMethod = makeMethod;
        };

        void call(Result* result) {
          printf ("Factory::Mediator::call(Result*)\n");
          (*makeMethod)(factory, result);
        };
    };
};

class A;

class B : private Factory {
  private:
    int v;

  public:
    B(int v) {
      printf ("B::B()\n");
      this->v = v;
    };

    int getV() const {
      printf ("B::getV()\n");
      return v;
    };

    static void makeCb(Factory* f, Factory::Result* a);

    Factory::Mediator make() {
      printf ("Factory::Mediator B::make()\n");
      return Factory::Mediator(static_cast<Factory*>(this), &B::makeCb);
    };
};

class A : private Factory::Result {
  friend class B;

  private:
    int v;

  public:
    A() {
      printf ("A::A()\n");
      v = 0;
    };

    A(Factory::Mediator fm) : Factory::Result(fm) {
      printf ("A::A(Factory::Mediator)\n");
    };

    int getV() const {
      printf ("A::getV()\n");
      return v;
    };

    void setV(int v) {
      printf ("A::setV(%i)\n", v);
      this->v = v;
    };
};

void B::makeCb(Factory* f, Factory::Result* r) {
      printf ("B::makeCb(Factory*, Factory::Result*)\n");
      B* b = static_cast<B*>(f);
      A* a = static_cast<A*>(r);
      a->setV(b->getV()+1);
    };

int main(int argc, char **argv) {
  B b(42);
  A a = b.make();
  printf ("a.v = %i\n", a.getV());
  return 0;
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X