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.