C++;:通过引用和复制构造函数返回 C++中的引用使我困惑不解。p>

C++;:通过引用和复制构造函数返回 C++中的引用使我困惑不解。p>,c++,reference,return-value-optimization,return-by-reference,C++,Reference,Return Value Optimization,Return By Reference,基本思想是,我试图从函数返回一个对象。我希望这样做时不返回指针(因为这样我就必须手动删除它),如果可能的话也不调用复制构造函数(为了提高效率,自然添加了:,还因为我想知道是否可以避免编写复制构造函数) 因此,总而言之,我发现了以下几种方法: 函数返回类型可以是类本身(MyClass-fun(){…})或类的引用(MyClass&fun(){…}) 函数可以在返回行构造变量(returnmyclass(a,b,c);)或返回现有变量(MyClass x(a,b,c);return x;) 接收变

基本思想是,我试图从函数返回一个对象。我希望这样做时不返回指针(因为这样我就必须手动
删除它),如果可能的话也不调用复制构造函数(为了提高效率,自然添加了:,还因为我想知道是否可以避免编写复制构造函数)

因此,总而言之,我发现了以下几种方法:

  • 函数返回类型可以是类本身(
    MyClass-fun(){…}
    )或类的引用(
    MyClass&fun(){…}
  • 函数可以在返回行构造变量(
    returnmyclass(a,b,c);
    )或返回现有变量(
    MyClass x(a,b,c);return x;
  • 接收变量的代码也可以有以下两种类型的变量:(
    MyClass x=fun();
    MyClass&x=fun();
  • 接收变量的代码可以动态创建新变量(
    MyClass x=fun();
    ),也可以将其分配给现有变量(
    MyClass x;x=fun();
对此有一些想法:

  • 使用返回类型
    MyClass&
    似乎不是一个好主意,因为这总是导致变量在返回之前被销毁
  • 复制构造函数似乎只在我返回现有变量时才会涉及。当返回在返回行中构造的变量时,它永远不会被调用
  • 当我将结果赋给现有变量时,析构函数也总是在返回值之前启动。此外,不会调用复制构造函数,但目标变量会接收函数返回的对象的成员值
这些结果如此不一致,以至于我感到完全困惑。那么,这里到底发生了什么?我应该如何正确构造并从函数返回对象?

推荐阅读:Scott Meyers。你可以在这里找到关于这个主题(以及更多)的很好的解释

简言之,如果按值返回,默认情况下将涉及复制构造函数和析构函数(除非编译器将它们优化掉——在某些情况下就是这样)

如果通过引用(或指针)返回一个本地变量(在堆栈上构造),则会带来麻烦,因为该对象在返回时会被破坏,因此会产生一个悬空引用

在函数中构造对象并返回它的规范方法是通过值,如:

MyClass fun() {
    return MyClass(a, b, c);
}

MyClass x = fun();
如果您使用它,就不需要担心所有权问题、悬空引用等,而且编译器很可能会为您优化额外的复制构造函数/析构函数调用,因此您也不需要担心性能

可以通过引用返回由
new
构造的对象(即在堆上)-从函数返回时不会销毁此对象。但是,您必须稍后通过调用
delete
明确地销毁它

在技术上,还可以将值返回的对象存储在引用中,如:

MyClass& x = fun();

然而,恐怕这样做没有多大意义。特别是因为人们可以很容易地将这种引用传递给程序中当前范围之外的其他部分;但是,
x
引用的对象是一个本地对象,一旦您离开当前范围,它将被销毁。因此,这种风格可能会导致严重的bug。

基本上,只有当对象离开方法后仍然存在时,返回引用才有意义。如果返回对正在销毁的内容的引用,编译器将发出警告

通过值返回引用而不是对象可以避免复制可能很重要的对象

引用比指针更安全,因为它们有不同的符号,但在幕后它们是指针。

阅读关于和NRVO的内容(总之,这两个词代表返回值优化,命名为RVO,是编译器用于实现您试图实现的目标的优化技术)


这里你会发现很多关于StAdvOpjs

< P>的主题。最好的理解C++中的拷贝的方法通常不是试图产生一个人工的例子和工具——编译器允许删除和添加拷贝构造函数调用,或多或少地符合它的要求。p>
底线-如果您需要返回一个值,请返回一个值,并且不必担心任何“费用”。

您会被以下两种情况所困扰:

1) 返回指针

MyClass*func(){ //一些笨蛋 返回新的MyClass(a、b、c); }

2) 返回对象的副本 MyClass func(){ 返回MyClass(a、b、c); }


返回引用无效,因为对象在退出func作用域后将被销毁,除非函数是类的成员并且引用来自作为类成员的变量。

根据您的用例,一种可能的解决方案是默认在函数外部构造对象,获取对它的引用,并在函数中初始化引用的对象,如下所示:

void initFoo(Foo& foo) 
{
  foo.setN(3);
  foo.setBar("bar");
  // ... etc ...
}

int main() 
{
  Foo foo;
  initFoo(foo);

  return 0;
}
现在,如果无法(或没有意义)默认构造
Foo
对象,然后稍后对其进行初始化,那么这当然不起作用。如果是这种情况,那么避免复制构造的唯一实际选择就是返回指向堆分配对象的指针


但是,想想你为什么要首先避免复制构造。复制构造的“费用”真的会影响您的程序吗?还是这是一种过早优化的情况?

关于返回引用唯一有意义的时间,是在返回对预先存在的对象的引用时。举个明显的例子,nea
MyClass foo(a, b, c);