clstaudt
clstaudt

Reputation: 22448

Combining multiple objects in a single return object

I want to return a signle object from a method call which contains multiple objects created in the method.

Results calculate() {

    Foo f;
    Bar b;
    ...
    Results r(f, b);
    return r;
}


class Results {

private:
    ?

public:

    Results(Foo& f, Bar& b);

    Foo? getFoo();

    Bar? getBar();

}

a) Should Results member variables be pointers?

private:
    Foo* foo;
    Bar* bar;


public:
    Results(Foo& f, Bar& b) {
        this->foo = &f;
        this->bar = &b;
    }

b) Should getFoo return Foo, Foo& or Foo*?

Upvotes: 0

Views: 369

Answers (5)

Skalli
Skalli

Reputation: 2817

I'm still a bit uncertain what you're trying to achieve, but currently you'd run into trouble: a and b are on the stack in calculate, you essentially return pointers to them. But as soon as calculate is finished and a and b go out of scope, you have wild pointers. That's very bad.
Create smart pointers and return them in an object if you have more return values.
Or pass pointers to a and b to calculate and create the objects on the heap with new in calculate. But be aware that you have to delete them by yourself otherwise you end up with memory leaks. So basically, if you have more than two objects in result, then add a smart pointer like std::auto_ptr or std::shared_ptr (or if you don't use C++11 boost::shared_ptr)

Upvotes: 0

Olaf Dietsche
Olaf Dietsche

Reputation: 74078

No, don't do it this way. Because in calculate(), Foo f and Bar b are local objects, which will go away, when you return from this function. Copy f and b into Results.

class Results {
private:
    Foo foo;
    Bar bar;

public:
    Results(const Foo& f, const Bar& b) : foo(f), bar(b) {}

    const Foo &getFoo() const { return foo; }
    const Bar &getBar() const { return bar; }
}

An easier way could be to use a std::pair and return that instead

std::pair<Foo, Bar> calculate() {
    Foo f;
    Bar b;
    ...
    return std::make_pair(f, b);
}

Upvotes: 2

utnapistim
utnapistim

Reputation: 27385

a) The results member variables should be held by value. Otherwise you return the addresses (or references) of local, out-of-scope data.

b) getFoo should return a const reference to the object, or return by value, in case of POD types.

That said, consider changing your interface to accept i/o parameters of the types Foo& and Bar&, populate the variables inside and not return them. This would avoid two copies of the returned values, which are necessary otherwise.

You can also replace your Results class with a std::tuple:

std::tuple<Foo,Bar> calculate() {

    Foo f;
    Bar b;
    ...
    return std::tuple(f,b);
}

// client code:
Foo foo;
Bar bar;

std::tie(foo, bar) = calculate();

Edit: If you do not use C++11 (for std::tuple) consider boost::tuple instead.

Upvotes: 0

user1795160
user1795160

Reputation:

a) No. By initializing the values in calculate(), these variables "die" when the function finished executing. This way, the pointers you initialized before will point to an empty space.

b) Considering the private members not to be pointers, you can do this as you want. If you want the data to "stay" inside the object, you may take pointers or references (does not matter which one). Take "normal" variables otherwise.

Upvotes: 1

mfontanini
mfontanini

Reputation: 21910

Use C++11's tuples(or boost's, otherwise), you're basically reimplementing them:

#include <tuple>

std::tuple<Foo, Bar> calculate() {
    Foo f;
    Bar b;
    ...
    return std::make_tuple(f, b);
}

void test() {
    Foo f;
    Bar b;
    std::tie(f, b) = calculate();
}

Note that this could be easily extended to return more than 2 objects. That's why I used std::tuple rather than std::pair as mentioned in another answer.

Upvotes: 4

Related Questions