【问题标题】:C++ ampersand receiving object from functionC++ & 从函数接收对象
【发布时间】:2012-04-10 22:31:19
【问题描述】:

我有以下简单的代码,其中包含一个普通构造函数和一个复制构造函数

class largeObj
{
public:
    largeObj()
    {
        printf("\nNormal constructor\n");
    }

    largeObj(const largeObj& mv)
    {
        printf("\nCopy constructor\n");
    }

    ~largeObj()
    {
        printf("\nDestroying..\n");
    }

    void tryme()
    {
        printf("\nHi :)\n");
    }

};

largeObj iReturnLargeObjects()
{
    largeObj md;


    return md;
}


int main()
{

    largeObj mdd = iReturnLargeObjects();

    mdd.tryme();

    return 0;
}

输出是

普通构造函数

复制构造函数

破坏..

嗨 :)

我明白了。

但如果我替换以下行

largeObj mdd = iReturnLargeObjects();

largeObj& mdd = iReturnLargeObjects();

输出都是一样的,这是为什么呢?

我的意思是:在第一种情况下不应该有另一个副本(没有 &)吗?这两条线有什么区别,为什么它们的行为相同?

【问题讨论】:

  • 第二个甚至不应该编译。它将临时绑定到对非常量的引用。
  • 我做了一些调试,发现iReturnLargeObjects()中的md对象调用了普通的构造函数,然后在return md时调用了复制构造函数;然后在从 iReturnLargeObjects() 返回之前对 md 对象调用析构函数。所以大概在内存中创建了另一个 largeObj。问题是:为什么 largeObj mdd = object 不创建另一个复制变量?这是因为 RVO 吗?所以这里的每个人都同意第二个 largeObj& mdd = 对象是完全非法的,并且由于一些奇怪的东西而编译
  • @jrok,它应该仍然可以编译。例如,如果您在堆栈上创建引用,则该引用不会被视为临时引用,因为编译器不会检查它的来源,只会检查它的有效引用,因此当它被返回时,它返回为一个参考。这仅是因为它是复制构造函数。

标签: c++ oop object


【解决方案1】:
largeObj& mdd = iReturnLargeObjects();

您不能将可变的左值引用绑定到右值。这是非法的 C++,只有某些特定的编译器扩展允许。然而,问题的语义没有改变,即使这个引用是 const 并且因此赋值是合法的。

您的输出没有什么不同的原因是因为称为 RVO 的编译器优化。这种优化在 C++ 标准中明确允许,允许编译器在某些限制内跳过它认为不必要的构造对象——即使这样做会改变程序的语义,这使得它成为一种非常不寻常的优化。

底线是:不要将副作用放在复制/移动构造函数和析构函数中,因为即使程序的正确性取决于调用它们,编译器也可以消除它们。

【讨论】:

  • 这里没有发生 RVO -- 如果有,您将看不到 Hi 之前的 Copy contructorDestorying 消息
  • 谢谢,那么函数返回类型呢?我的意思是:largeObj& myFunction();返回引用而不是简单的 largeObj 更好还是感谢 RVO 我可以忘记这些事情并简单地返回 largeObj?
【解决方案2】:

您使用的是什么编译器,我敢打赌这行得通,因为无论您使用什么编译器都不会在返回后将未使用的堆栈变量清零,您没有遇到段错误,因为它仍然是您的堆栈。基本上 md 留在堆栈上,从技术上讲,您仍然可以解决。两者的区别是

largeObj md;

是一个完整地接收变量的变量。

largeObj& mdd = iReturnLargeObjects();

是对由于惰性编译器而仍然存在的变量的引用。

【讨论】:

  • 现在这很奇怪,Visual Studio 通常很擅长做所有不属于标准的事情,但可以防止编码人员犯愚蠢的错误。基本上这是可行的,因为复制构造函数按定义返回引用。并且堆栈没有被清除。在调用 mdd.tryme() 之前添加此函数以了解我的意思。 int messupstack(){char* a = "llllllllllllllllllllll'";返回 5; }
  • 等等,我很迟钝,最后一节不会做任何事情,因为 tryme 只是打印出不在类实例中的数据。您需要类变量才能看到不正确的结果。
猜你喜欢
  • 1970-01-01
  • 2021-04-04
  • 1970-01-01
  • 2021-08-09
  • 1970-01-01
  • 2011-09-05
  • 1970-01-01
  • 2015-11-22
  • 1970-01-01
相关资源
最近更新 更多