How to return a class object by reference in C++?
Asked Answered
M

6

55

I have a class called Object which stores some data.

I would like to return it by reference using a function like this:

    Object& return_Object();

Then, in my code, I would call it like this:

    Object myObject = return_Object();

I have written code like this and it compiles. However, when I run the code, I consistently get a seg fault. What is the proper way to return a class object by reference?

Mathes answered 18/1, 2012 at 17:27 Comment(3)
shouldn't myObject be a referrence?Stephniestepladder
You can find the answer here: #3350885Clamber
you can find the ways here: #3350885Clamber
I
67

You're probably returning an object that's on the stack. That is, return_Object() probably looks like this:

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

    return object_to_return;
}

If this is what you're doing, you're out of luck - object_to_return has gone out of scope and been destructed at the end of return_Object, so myObject refers to a non-existent object. You either need to return by value, or return an Object declared in a wider scope or newed onto the heap.

Indign answered 18/1, 2012 at 17:30 Comment(0)
H
28

You can only use

     Object& return_Object();

if the object returned has a greater scope than the function. For example, you can use it if you have a class where it is encapsulated. If you create an object in your function, use pointers. If you want to modify an existing object, pass it as an 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;
         }
   };
Hoopen answered 18/1, 2012 at 17:40 Comment(2)
But what happens if, in my calling code I say: MyClass mc; Object outsideObj = mc.return_Object; If I then modify properties of outsideObj does that actually modify the myObj that is encapsulated inside mc?Vagary
Object outsideObj = mc.return_Object(); will provoke a copy construction of the instance outsideObj. It is now a separate instance, and whether modifying one would affect the other depend on how the copy constructor has been implemented.Hoopen
K
18

You can only return non-local objects by reference. The destructor may have invalidated some internal pointer, or whatever.

Don't be afraid of returning values -- it's fast!

Kristenkristi answered 18/1, 2012 at 17:28 Comment(0)
H
11

I will show you some examples:

First example, do not return local scope object, for example:

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

You can't return local by reference, because local is destroyed at the end of the body of dontDoThis.

Second example, you can return by reference:

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

Here, you can return by reference both s1 and s2 because they were defined before shorterString was called.

Third example:

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

usage code as below:

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

get_val can return elements of s by reference because s still exists after the call.

Fourth example

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;
}

usage example:

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

Fifth example:

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;
}

You could then use the operator= above like this:

String a;
String b;
String c = b = a;
Ham answered 30/5, 2018 at 1:34 Comment(0)
E
1

Well, it is maybe not a really beautiful solution in the code, but it is really beautiful in the interface of your function. And it is also very efficient. It is ideal if the second is more important for you (for example, you are developing a library).

The trick is this:

  1. A line A a = b.make(); is internally converted to a constructor of A, i.e. as if you had written A a(b.make());.
  2. Now b.make() should result a new class, with a callback function.
  3. This whole thing can be fine handled only by classes, without any template.

Here is my minimal example. Check only the main(), as you can see it is simple. The internals aren't.

From the viewpoint of the speed: the size of a Factory::Mediator class is only 2 pointers, which is more that 1 but not more. And this is the only object in the whole thing which is transferred by value.

#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;
}
Exhort answered 26/6, 2016 at 14:12 Comment(0)
S
0

It isn't really good practice to return an initiated object as it does go out of scope. There are rare instances that this is the desired option. It actually can be done if the class is a referencing counting smart pointer or some other smart pointer. How does a reference-counting smart pointer's reference counting work?

Scottiescottish answered 13/4, 2021 at 15:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.