web-dev-qa-db-fra.com

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

J'ai une classe appelée Object qui stocke des 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 du code comme celui-ci et il se compile. Cependant, lorsque j'exécute le code, j'obtiens systématiquement une erreur de segmentation. Quelle est la bonne façon de renvoyer un objet classe par référence?

32
user788171

Vous retournez probablement un objet qui est sur la pile. Autrement dit, return_Object() ressemble probablement à ceci:

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 hors de portée et a été détruit à la fin de return_Object, donc myObject fait référence à un objet inexistant. Vous devez soit renvoyer par valeur, soit renvoyer un Object déclaré dans une portée plus large ou newed sur le tas.

46
Chowlett

Vous ne pouvez utiliser que

     Object& return_Object();

si l'objet renvoyé a une portée plus grande que la fonction. Par exemple, vous pouvez l'utiliser si vous avez une classe où elle est encapsulée. Si vous créez un objet dans votre fonction, utilisez des pointeurs. Si vous souhaitez 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;
         }
   };
21
UmNyobe

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

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

15
spraff

Eh bien, ce n'est peut-être pas une très belle solution dans le code, mais c'est vraiment beau dans l'interface de votre fonction. Et c'est aussi très efficace. Elle est idéale si la seconde est plus importante pour vous (par exemple, vous développez une bibliothèque).

L'astuce est la suivante:

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

Voici mon exemple minimal. Ne cochez que la main(), comme vous pouvez le voir, c'est simple. Les internes ne le sont pas.

Du point de vue de la vitesse: la taille d'un Factory::Mediator classe n'est que de 2 pointeurs, ce qui est plus que 1 mais pas plus. Et c'est le seul objet de l'ensemble qui soit transféré par la 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;
}
2
peterh

Je vais vous montrer quelques exemples:

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

const string &mainip(const string &s)
{
    string ret=s;

    //  operator ret

    return ret;
}

vous ne pouvez pas retourner ret par référence, car ret est détruit à la fin.

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

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

vous pouvez retourner par référence pour s1 et s2 est toujours existant.

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

car après retour par référence, l'objet existe toujours;

Quatrième exemple

class Student{
    public:
        string m_name;
        int age;    

        string& getName();
};

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

// you can return by reference, after this function stu object is still exists
string& Test(Student &stu)
{
    return stu.m_name;
}

exemple d'utilisation:

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

Cinquième exemple:

class String{
    private:
        char* str_;

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

// for example a=b=c usage
String& String::operator =(const String &str)   
{
    if (this == &str)
    {
        return *this;
    }
    delete [] str_;
    int len = strlen(str.str_);
    str_ = new char[len+1];
    strcpy(str_,str.str_);
    return *this;
}
1
Jayhello