user2058002
user2058002

Reputation:

Returning a class in C++

In Java, where all classes are really references, I do this:

Car getCar(int mileage)
{
  Car car = new Car();

  car.setMileage(mileage);
  return car;
}

In C++, how do I do this? I can put it into a reference:

void getCar(int mileage, Car& car)
{
  car.setMileage(mileage);
  return true;
}

Or, I can create a new object:

Car* getCar(int mileage)
{
  Car* car = new Car;

  car.setMileage(mileage);
  return car;
}

But then, the caller is also responsible for deleting car.

I don't want to return a pointer. I want to return a Car:

Car getCar(int mileage)
{
  Car car;

  car.setMileage(mileage);
  return car;
}

But of course, car is a local variable which will be deleted once the function finishes.

What's generally the 'standard' way of doing this? Which is the best way, and why?

Upvotes: 2

Views: 124

Answers (4)

Emilio Garavaglia
Emilio Garavaglia

Reputation: 20730

The important difference to be well understood is that in Java objects are by reference, and identified by their address.

In C++ they are "values" (like int) and ... identified by their address :-( (this is how the specs calls an object to be)

Whatever the idioms says about, you have to take a decision: what is important to you: the value or the address?

Two Person having a same name value are representing a same (in real world) person, or two real-word homonym person-s? How important is polymorphism in your context? (Are there many kind of "person-s" represented by different Person-derived)?

This is not something "idioms" say. It is a convention you have to establish.

If you want to work with values, return by value (note: copy-on-return is elided by the compiler in the most of the cases, so it's not a performance limiter), but forgot polymorphism. But if your object contain some resources, ensure they also follow the "by value" paradigm, otherwise be prepared to override constructor, destructor, copy-constructor, move-constructor and assignment to properly handle the resource ownership. Or... use a by-value resource manager to wrap them around (smart pointers can do this).

If you want to work with addresses, and need polymorphism, than be prepared to allocate objects on heap, work with pointers, and manage allocation / deallocation properly (eventually with the help of smart pointers like unique_ptr or shared_ptr).

For both of the cases there are a series of idioms and patterns, but the very first point is understanding what model are you targeting, and behave coherently.

But one thing you will in any case not obtain: C++ will never be "like java". So don't try to emulate it. By value or by pointer (by reference doesn't change a lot) you have to think in another way.

Upvotes: 0

Robᵩ
Robᵩ

Reputation: 168606

Your final example is the correct and idiomatic way to return an object:

Car getCar(int mileage)
{
  Car car;

  car.setMileage(mileage);
  return car;
}

Yes, car will be deleted at the end of the function, but not before it is copied into the returned object. You might invoke this function like so:

{
    Car myCar;
    myCar = getCar(42);
}

The car that is local to getCar is copied into the calling environment's myCar.


What you can't and must not do is to return a reference to or a pointer to a local variable. This is WRONG:

Car& getCar(int mileage)
{
  Car car;
  return car;
}

This is also WRONG:

Car* getCar(int mileage)
{
  Car car;
  return &car;
}

In each of these cases, you are allowing the calling function access to an object which no longer exists.

You mustn't return a pointer or reference to a local. You may return a copy of a local.

Upvotes: 2

Tony Delroy
Tony Delroy

Reputation: 106068

Car getCar(int mileage) {   Car car = new Car();    car.setMileage(mileage);   return car; }

In C++, how do I do this? I can put it into a reference:

void getCar(int mileage, Car& car) {   car.setMileage(mileage);   return true; }

Yes - that's ok, though you can't return anything for a void function.

Or, I can create a new object:

Car* getCar(int mileage) {   Car* car = new Car;    car.setMileage(mileage);   return car; }

But then, the caller is also responsible for deleting car.

That's true... but you can use a smart pointer to make that more reliable and convenient.

I don't want to return a pointer. I want to return a Car:

Car getCar(int mileage) {   Car car;    car.setMileage(mileage);   return car; }

But of course, car is a local variable which will be deleted once the function finishes.

Not true - this works perfectly - the car object is returned by value, which means it's available on the stack for the caller to use in expressions, including as a source for copying elsewhere.

What's generally the 'standard' way of doing this? Which is the best way, and why?

Generally, that last version above is the "standard" way - it is usually efficient enough (often optimal with optimised builds), avoids messy pointers, and is intuitive and simple. Certainly, going from C++ where you can do that to Java where you need to new variables all over the place is painful.

Upvotes: 1

Jerry Coffin
Jerry Coffin

Reputation: 490048

Your last piece of code is fine -- you're returning the value of the local variable, not the local variable itself, so what happens is that (at least in theory) that value will be copied from the local variable into wherever the caller assigns it.

This is basically the same as if I have something like:

int f() { 
    int x = 0; 
    return x;
}

Again, we're returning a value. The value happens to come from a local variable, but we're still returning the value, not the variable.

As far as the "at least in theory" part goes, most compilers can optimize this so there won't really be an copying going on at all. There are some special rules to allow the compiler to skip doing this copying, even if the copying would have externally visible side effects that you'd expect to see when the copying happened (e.g., if you write a copy constructor that printed something out when the copy happened).

Upvotes: 4

Related Questions