由于范围问题,我对此感到疑惑。例如,考虑代码
typedef struct {
int x1;/*top*/
int x2;/*bottom*/
int id;
} subline_t;
subline_t subline(int x1, int x2, int id) {
subline_t t = { x1, x2, id };
return t;
}
int main(){
subline_t line = subline(0,0,0); //is line garbage or isn't it? the reference
//to subline_t t goes out of scope, so the only way this wouldn't be garbage
//is if return copies
}
所以我的问题是,返回语句是否总是复制?在这种情况下,它似乎工作,所以我被引导相信返回确实复制。如果它复制了,它会在每种情况下复制吗?
答案 0 :(得分:40)
是的,在这种情况下会有一份副本。如果你改变了这样的函数声明:
subline_t &subline(int x1, int x2, int id) {
然后不会复制。但是,在您的特定情况下,返回对堆栈上分配的对象的引用是无效的。问题是在调用者有机会使用它之前,该对象将被破坏并失效。
这与C ++的常见Return Value Optimization有关,可以避免在您描述的情况下执行实际的复制操作。最终结果是(或应该)与复制完成时相同,但您应该了解优化。在某些情况下,此优化的存在可能会改变程序的可观察行为。
答案 1 :(得分:4)
在您的情况下,它将返回一份副本
如果您的代码是
subline_t& subline(int, int)
然后它将返回一个引用,这将产生未定义的行为。
答案 2 :(得分:3)
是的,对于声明为返回struct
的函数,此类结构的return
将复制它(尽管编译器有权优化副本,主要是在它可以证明优化在语义上是无害的,你可以推断“好像”复制是有保证的。)
但是,既然 将此标记为C ++而不是C,为什么不为struct
提供构造函数,而不是......?看起来更清晰,更直接......! - )
答案 3 :(得分:3)
它将始终返回副本。
如果要避免在返回时复制对象的性能损失,可以声明指针,使用new构建对象的实例,然后返回指针。在这种情况下,将复制指针,但对象不会被复制。
答案 4 :(得分:2)
是的,返回是一个副本
subline_t subline(int x1, int x2, int id) {
subline_t t = { x1, x2, id };
return t;
}
如果你把一个引用者,那么它不是一个副本
subline_t & subline(int x1, int x2, int id) {
subline_t t = { x1, x2, id };
return t; // will result in corruption because returning a reference
}
答案 5 :(得分:1)
通过值而不是通过引用来完成C ++中的对象。
对subline_t t的引用超出了范围
不,该对象已被复制。
将返回语句始终复制
是但不是......在语义上它的行为与复制相似,但有一些称为返回值优化的东西可以保存复制构造函数。
foo make_foo()
{
foo f(1,2,3);
return f;
}
foo ff=make_foo(); /// ff created as if it was created with ff(1,2,3) -- RVO
foo ff2;
ff2=make_foo(); /// instance of foo created and then copied to ff2 and then old
/// instance destroyed
答案 6 :(得分:1)
它返回一个副本,这是你想要它做的。更改它以返回引用将导致在赋值行中的未定义行为。
但是,在C ++中执行此操作的惯用方法是使用构造函数和赋值列表。这样可以更好地封装代码和数据结构,并允许您避免编译器可以自由构造/销毁/复制的过多中间对象。
struct subline_t {
int x1;/*top*/
int x2;/*bottom*/
int id;
// constructor which initialises values with assignment list.
subline_t(int the_x1, int the_x2, int the_id) :
x1(the_x1),
x2(the_x2),
id(the_id)
{
}
};
int main(){
subline_t line2(0,0,0); // never requires a copy or assignment.
}
答案 7 :(得分:0)
对于您已定义的结构subline_t
,是的,它将始终返回副本。
答案 8 :(得分:0)
返回的类或结构可能会也可能不会被复制,具体取决于编译器是否使用了copy elision。查看What are copy elision and return value optimization? 的答案。简而言之,它是否被复制取决于许多事情。
您当然可以通过返回引用来避免副本。在您的示例中,返回引用无效(尽管编译器将允许它),因为本地结构已在堆栈上分配,因此返回的引用引用了已释放的对象。但是,如果对象被传递给您的函数(直接或作为对象的成员),您可以安全地返回对它的引用并避免复制返回。
最后,如果您不能信任复制省略并且您想避免复制,则可以使用并返回unique_ptr
而不是引用。虽然unique_ptr
本身可能是也可能不是(同样,取决于复制省略!),但不会复制对象本身。然而,如果unique_ptr
的复制省略由于某种原因没有发生,则复制/移动unique_ptr
非常便宜。
以下是使用unique_ptr
的示例:
#include <memory>
struct A {
public:
int x;
int y;
A(int x, int y) : x(x), y(y) {
}
};
std::unique_ptr<A> returnsA() {
return std::make_unique<A>(3, 4);
}
int main() {
auto a = returnsA();
}
请注意,您必须(不幸地)为您的struct声明一个构造函数,否则make_unique
将因C ++的不足而无法编译。
答案 9 :(得分:-1)
仅供参考,因为在这种情况下,您只使用结构(ure),它与C语言中的行为相同。
虽然这是一种语言功能,但建议不要使用
答案 10 :(得分:-2)
我不同意也不建议返回向量以更改值: 是更快的通过作为参考:
void vectorial(vector <double> a, vector <double> b, vector <double> &c)
{
c[0] = a[1] * b[2] - b[1] * a[2]; c[1] = -a[0] * b[2] + b[0] * a[2]; c[2] = a[0] * b[1] - b[0] * a[1];
}
//This is slow!!!:
vector <double> vectorial(vector <double> a, vector <double> b)
{
vector <double> c{ a[1] * b[2] - b[1] * a[2], -a[0] * b[2] + b[0] * a[2], a[0] * b[1] - b[0] * a[1] };
return c;
}
我在VS2015上进行了测试,并在发布模式下获得了以下结果:
参考:8.01 MOP和返回向量:5.09 MOP更差60%!
在调试模式下,情况更糟:
参考:0.053 MOPS,返回向量:0.034 MOPs