0

I'm currently learning how to manipulate objects with functions in C++, and so far I have come to the following point:

If your object is potentially large, don't make it a local variable, i.e. keep it in the heap to save time on copying.

Specifically, I'm interested in a scenario where we are using a function that creates an object which did not exist before. I have put together the following small example to showcase what to do if you have a function:

#include <iostream>
using namespace std;

struct obj {
    int first;
    int second;
};


void setFirstVersionA(int a, obj * s)
{
    s->first = a;
}

obj * setFirstVersionB(int a)
{
    //in Version B I am creating a new object in the function but have to run delete outside
    obj * s = new obj();
    s->first = a;
    return s;
}


int main(int argc, const char** argv)
{
    obj * myobj = new obj();
    setFirstVersionA(2,myobj);
    //now I have the new obj in *i
    cout << myobj->first;
    delete myobj;

    //this is an alternative to passing a pointer directly:

    //no need to re-declare the pointer as delete only erases the myobj data
    myobj = setFirstVersionB(3);
    //now I have the new obj in *i
    cout << myobj->first;
    delete myobj;

    return 0;
}

As far as I can tell, both functions achieve the same result.

I like version A better because it does not separate the new and delete declarations, and makes me less prone of forgetting to delete the object once I'm done. But it's a return type void and I find the code less readable because I have to actually check what the function does (and in general it means reading some other file).

I like version B better because it returns "the thing" that I wanted to change. So I know immediately, this function changes that guy (in this case the obj s). But it separates, new and delete. Which honestly, I find less terrible than having a series of void functions in my code and not seeing immediately what they do. Also, a lot has been written here about not returning pointers to local variables, but in variant B, though the object is created within a function, it is not a local variable (as it sits in the heap). Right?

Is there a better way to do it? Also, a "function creating an object which did not exist before" sounds a lot like a constructor. :) Should I be perhaps creating a class with a constructor for each of my objects?

Thanks for your advice!

4
  • 1
    You should probably read about smart pointers, and about passing object by reference. Commented Jul 23, 2013 at 12:01
  • 2
    You should definitely read up on return value optimization, and C++11 move semantics. Commented Jul 23, 2013 at 12:03
  • 1
    Apart, obviously, from what @Antonio said, you should also check RVO and NRVO. And, by the way, you can always return obj * from the first function, if it is a question of readability for you. Commented Jul 23, 2013 at 12:05
  • @juanchopanza Interesting article! Commented Jul 23, 2013 at 12:23

4 Answers 4

3

The proper way would probably be to either create a constructor that takes the value as an argument:

struct obj
{
    obj(int f) : first(f) {}
    // ...
};

// ...

obj myobj(2);

Or to have a setter function:

struct obj
{
    void set_first(int f) { first = f; }
    // ...
};

// ...

obj myobj;
myobj.set_first(2);

The above methods can of course be combined, so you both have a specialized constructor and a setter method.

Although the setter method may be skipped since you are using a structure with only public member variables.

Sign up to request clarification or add additional context in comments.

1 Comment

I would suggest to add why this is the "proper" way ?
3

You should disregard the advice you found, allocate the object on the stack, and return it by value. C++, especially C++11, has specific optimizations to make this efficient: copy elision (various circumstances allow the compiler to act as if two objects were actually one) and move semantics (new in C++11, allow the compiler to recognize situations where the old object is no longer needed and do something more efficient than copying).

2 Comments

Very interesting, so basically you are saying that in this particular example, I can forget about pointers, return the object by value and the compiler will optimize the code to run as fast as with pointers?
Generally yes - move semantics help, and depends on the compiler. If you have 'default' keyword support you can get some further clever optimisations too
2

I suppose you come fram the Java world (obj * myobj = new obj(); syntax) It is correct in C++ but you should not use pointers when it's not necessary.

A better (imo) approch looks like:

int main(int argc, const char** argv){
    Obj myObject; // your object now exists, fully usable.
    myObject.setValue(42); //classic, java-like setter

    Obj mySecondObect(42); //even better when you know the value at construct time.
}

the ctr for Obj would looks like:

Obj::Obj(int myValue) : _myVal(myvalue){}

in this case, your attribut is initialized before the constructor body (see the c++ constructor cycle).

Comments

1

Since you're learning C++, I'd suggest learning c++11. In which case you should really be thinking about smart pointers (such as std::unique_ptr) or probably even more about creating items on the stack and let the compiler take care of auto destruction - RAII (Resouce aquisition is initialisation) principles.

That way you'll avoid your new/delete potential memory leaks, and be creating more reliable code.

A function creating objects is typically a factory, so later on you might want to look at boost:value_factory<> as a cleaner way of doing this too.

4 Comments

I thought unique_ptr came out in c++11 according to en.wikipedia.org/wiki/Smart_pointer? Maybe not?
And both unique_ptr variants are marked as C++11 only. That mark does not only apply to the Deleter argument. unique_ptr is new in C++11, which makes sense, because it depends on move semantics, which are also new.
Ah, yes, sorry, you are all right and I was wrong: I was confusing unique_ptr with (the now deprecated) auto_ptr
At this point, the only thing that I would like to add is that if one cannot use C++11, boost provides alternatives for smart pointers.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.