简体   繁体   中英

Functions creating objects in C++

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, ie 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!

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.

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).

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).

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM