如何在C ++中“返回一个对象”?
我知道这个标题听起来很熟悉,因为有很多类似的问题,但是我要求问题的另一个方面(我知道堆栈中的东西和将它们放在堆上的区别)。
在Java中,我总是可以返回对“本地”对象的引用
public Thing calculateThing() { Thing thing = new Thing(); // do calculations and modify thing return thing; }
在C ++中,为了做类似的事情,我有两个选项
(1)当我需要“返回”一个对象时,我可以使用引用
void calculateThing(Thing& thing) { // do calculations and modify thing }
然后像这样使用它
Thing thing; calculateThing(thing);
(2)或者我可以返回一个指向动态分配的对象的指针
Thing* calculateThing() { Thing* thing(new Thing()); // do calculations and modify thing return thing; }
然后像这样使用它
Thing* thing = calculateThing(); delete thing;
使用第一种方法,我不需要手动释放内存,但对我来说,它使代码难以阅读。 第二种方法的问题是,我将不得不记得delete thing;
,看起来不太好。 我不想返回一个复制的值,因为效率不高(我想),所以在这里出现问题
- 有没有第三个解决方案(不需要复制值)?
- 如果我坚持第一个解决方案,有什么问题吗?
- 何时以及为什么要使用第二种解决方案?
我不想返回复制的值,因为效率不高
证明给我看。
查找RVO和NRVO,并在C ++ 0x移动语义。 在大多数情况下,在C ++ 03中,out参数只是让代码变得丑陋的一个好方法,而在C ++ 0x中,实际上会使用out参数来伤害自己。
只要写干净的代码,按价值返回。 如果性能是一个问题,请将其归档(不要猜测),并找出可以解决的问题。 它可能不会从函数返回的东西。
也就是说,如果你死定了这样的写作,你可能会想要做out的参数。 它避免了动态内存分配,这是更安全,通常更快。 它确实需要你在调用函数之前有一些方法来构造对象,这对于所有的对象并不总是有意义的。
如果你想使用动态分配,可以做的最少的是把它放在一个智能指针。 (无论如何都应该始终这样做)然后你不必担心删除任何东西,事情是异常安全的等等。唯一的问题是它可能比按值返回更慢!
只需创建该对象并将其返回
Thing calculateThing() { Thing thing; // do calculations and modify thing return thing; }
我想如果你忘记了优化,只是编写可读的代码(你需要稍后运行一个分析器 – 但不要预先优化),那么你会为自己做一个忙。
你有没有尝试使用智能指针(如果事情真的是大而重的对象),就像auto_ptr:
std::auto_ptr<Thing> calculateThing() { std::auto_ptr<Thing> thing(new Thing); // .. some calculations return thing; } // ... { std::auto_ptr<Thing> thing = calculateThing(); // working with thing // auto_ptr frees thing }
只要返回一个像这样的对象:
Thing calculateThing() { Thing thing(); // do calculations and modify thing return thing; }
这将调用东西的复制构造函数,所以你可能想要做你自己的实现。 喜欢这个:
Thing(const Thing& aThing) {}
这可能会执行一些慢,但它可能不是一个问题。
更新
编译器可能会优化对拷贝构造函数的调用,所以不会有额外的开销。 (如评论中指出的dreamlax)。
确定是否正在调用复制构造函数的一个快速方法是将日志记录添加到类的复制构造函数中:
MyClass::MyClass(const MyClass &other) { std::cout << "Copy constructor was called" << std::endl; } MyClass someFunction() { MyClass dummy; return dummy; }
调用一些someFunction
; 你将得到的“Copy构造函数被调用”的行数将在0,1和2之间变化。如果你没有得到,那么你的编译器已经优化了返回值(它被允许)。 如果你没有得到0,而你的拷贝构造函数是非常昂贵的, 那么搜索另外的方法来从你的函数中返回实例。
我相信C ++专家会有更好的答案,但是我个人喜欢第二种方法。 使用智能指针有助于解决忘记delete
的问题,正如你所说,它看起来比先前创建一个对象更清洁(如果你想在堆上分配它,仍然必须删除它)。
首先你在代码中有一个错误,你的意思是有Thing *thing(new Thing());
,只有return thing;
。
- 使用
shared_ptr<Thing>
。 这是一个指针。 当所包含的最后一个引用超出范围时,它将被删除。 - 第一个解决方案在天真的库中很常见。 它有一些性能和语法开销,如果可能的话避免它
- 只有当你可以保证不会抛出异常,或者当性能是绝对关键的时候,你才能使用第二种解决方案(甚至在与之相关之前,你将与C或者汇编接口)。