C ++方法链接导致析构函数被调用两次
C++ Method chaining causes destructor to be called twice
我有一个使用方法链的简单 C++ 程序,我注意到析构函数仅在链调用中使用时被调用两次。仅当链调用也包含构造函数时才会发生这种情况。如果单独调用析构函数只调用一次。
代码如下:
class Foo {
public:
Foo() { cout << "-- constructor " << this << endl; }
~Foo () { cout << "-- destructor " << this << endl; };
Foo& bar() {
cout << "---- bar call " << this << endl;
return *this;
}
};
int main() {
cout << "starting test 1" << endl;
{
Foo f = Foo();
}
cout << "ending test 1" << endl << endl;
cout << "starting test 2" << endl;
{
Foo f = Foo().bar();
}
cout << "ending test 2" << endl;
return 0;
}
本申请结果如下:
starting test 1
-- constructor 0x7ffd008e005f
-- destructor 0x7ffd008e005f
ending test 1
starting test 2
-- constructor 0x7ffd008e005f
---- bar call 0x7ffd008e005f
-- destructor 0x7ffd008e005f
-- destructor 0x7ffd008e005f
ending test 2
这是标准行为吗(如果是,为什么会这样?)还是我犯了什么错误?我可以阻止这种情况吗?
Foo f = Foo().bar();
还调用 Foo
的 复制构造函数 ,这是当前编译器生成的构造函数,因此不会向控制台输出任何内容. 这就是为什么看起来你调用的析构函数多于构造函数的原因。
您可以写 const Foo& f = Foo().bar();
来避免复制。使用 const
还可以延长匿名临时文件的生命周期,这很好。
请注意,行中涉及两个 Foo
类型的对象:
Foo f = Foo().bar();
^----------------this one
^------------and this one
然而,一个是通过构造函数创建的,另一个是通过复制构造函数创建的。这就是为什么你只打印一行用于构建而两行用于销毁。代码一切正常,您只需实现复制构造函数即可看到一致的输出。
这是预期的行为。即使您 return 来自 bar
的引用,您也没有使用它。
Foo f = Foo().bar();
按值捕获 return 值,以便您制作副本。这意味着来自 Foo()
的 Foo
在表达式的末尾被销毁,并且当您退出范围时,您制作的 f
的副本被销毁。
还有一个构造函数你没有自己实现。复制构造函数。
你的调用 Foo f = Foo().bar();
可以写成 Foo tmp = Foo(); Foo f = tmp.bar();
只有 tmp
对象的实例化才会调用构造函数。 f
调用的构造函数是自动生成的拷贝构造函数。
这应该会给你一些更好的输出:
#include <iostream>
using std::cout;
using std::endl;
class Foo {
public:
Foo() { cout << "-- constructor " << this << endl; }
Foo(const Foo& f) { cout << "-- copy-constructor " << this << endl; }
~Foo() { cout << "-- destructor " << this << endl; };
Foo& bar() {
cout << "---- bar call " << this << endl;
return *this;
}
};
int main() {
cout << "starting test 1" << endl;
{
Foo f = Foo();
}
cout << "ending test 1" << endl << endl;
cout << "starting test 2" << endl;
{
Foo f = Foo().bar();
}
cout << "ending test 2" << endl;
return 0;
}
--
starting test 1
-- constructor 000000EC09CFF944
-- destructor 000000EC09CFF944
ending test 1
starting test 2
-- constructor 000000EC09CFFA44
---- bar call 000000EC09CFFA44
-- copy-constructor 000000EC09CFF964
-- destructor 000000EC09CFFA44
-- destructor 000000EC09CFF964
ending test 2
我有一个使用方法链的简单 C++ 程序,我注意到析构函数仅在链调用中使用时被调用两次。仅当链调用也包含构造函数时才会发生这种情况。如果单独调用析构函数只调用一次。
代码如下:
class Foo {
public:
Foo() { cout << "-- constructor " << this << endl; }
~Foo () { cout << "-- destructor " << this << endl; };
Foo& bar() {
cout << "---- bar call " << this << endl;
return *this;
}
};
int main() {
cout << "starting test 1" << endl;
{
Foo f = Foo();
}
cout << "ending test 1" << endl << endl;
cout << "starting test 2" << endl;
{
Foo f = Foo().bar();
}
cout << "ending test 2" << endl;
return 0;
}
本申请结果如下:
starting test 1
-- constructor 0x7ffd008e005f
-- destructor 0x7ffd008e005f
ending test 1
starting test 2
-- constructor 0x7ffd008e005f
---- bar call 0x7ffd008e005f
-- destructor 0x7ffd008e005f
-- destructor 0x7ffd008e005f
ending test 2
这是标准行为吗(如果是,为什么会这样?)还是我犯了什么错误?我可以阻止这种情况吗?
Foo f = Foo().bar();
还调用 Foo
的 复制构造函数 ,这是当前编译器生成的构造函数,因此不会向控制台输出任何内容. 这就是为什么看起来你调用的析构函数多于构造函数的原因。
您可以写 const Foo& f = Foo().bar();
来避免复制。使用 const
还可以延长匿名临时文件的生命周期,这很好。
请注意,行中涉及两个 Foo
类型的对象:
Foo f = Foo().bar();
^----------------this one
^------------and this one
然而,一个是通过构造函数创建的,另一个是通过复制构造函数创建的。这就是为什么你只打印一行用于构建而两行用于销毁。代码一切正常,您只需实现复制构造函数即可看到一致的输出。
这是预期的行为。即使您 return 来自 bar
的引用,您也没有使用它。
Foo f = Foo().bar();
按值捕获 return 值,以便您制作副本。这意味着来自 Foo()
的 Foo
在表达式的末尾被销毁,并且当您退出范围时,您制作的 f
的副本被销毁。
还有一个构造函数你没有自己实现。复制构造函数。
你的调用 Foo f = Foo().bar();
可以写成 Foo tmp = Foo(); Foo f = tmp.bar();
只有 tmp
对象的实例化才会调用构造函数。 f
调用的构造函数是自动生成的拷贝构造函数。
这应该会给你一些更好的输出:
#include <iostream>
using std::cout;
using std::endl;
class Foo {
public:
Foo() { cout << "-- constructor " << this << endl; }
Foo(const Foo& f) { cout << "-- copy-constructor " << this << endl; }
~Foo() { cout << "-- destructor " << this << endl; };
Foo& bar() {
cout << "---- bar call " << this << endl;
return *this;
}
};
int main() {
cout << "starting test 1" << endl;
{
Foo f = Foo();
}
cout << "ending test 1" << endl << endl;
cout << "starting test 2" << endl;
{
Foo f = Foo().bar();
}
cout << "ending test 2" << endl;
return 0;
}
--
starting test 1
-- constructor 000000EC09CFF944
-- destructor 000000EC09CFF944
ending test 1
starting test 2
-- constructor 000000EC09CFFA44
---- bar call 000000EC09CFFA44
-- copy-constructor 000000EC09CFF964
-- destructor 000000EC09CFFA44
-- destructor 000000EC09CFF964
ending test 2