问题描述
我有一个类A
,我可以不使用它而构造一个std::function
。在破坏时,应该调用给定的函数(如果有的话)。我的问题是,该对象在创建并由我的getSomeA()
函数返回后被销毁,该函数在应该被调用之前调用std::function
。传递给构造函数的函数仅应调用一次。一些示例代码:
#include <iostream>
#include <functional>
static void testFunction(const std::string& msg)
{
std::cout << "TestFunction: " << msg << "\n";
}
class A {
public:
A(void) = default;
A(const std::function<void()>& onDestroy) :
onDestroy(onDestroy)
{ }
~A(void)
{
if (onDestroy) onDestroy();
else std::cout << "in dtor but no onDestroy was set\n";
}
private:
std::function<void()> onDestroy;
};
A getSomeA(void)
{
return A(std::bind(testFunction,"the A that was created inside getSomeA"));
}
int main(void)
{
A b1;
std::cout << "After creating b1\n";
b1 = getSomeA();
std::cout << "After reassigning b1\n";
std::cout << "Here the program works with b1...\n";
}
程序输出
After creating b1
TestFunction: the A that was created inside getSomeA
After reassigning b1
Here the program works with b1...
TestFunction: the A that was created inside getSomeA
因此该函数在应该被调用之前(在int main()的末尾)被调用。
添加了move构造函数和赋值运算符后,所有操作均按预期进行:
A(A&& other) :
onDestroy(std::exchange(other.onDestroy,nullptr))
{ }
A& operator=(A&& other)
{
onDestroy = std::exchange(other.onDestroy,nullptr);
return *this;
}
程序输出
After creating b1
in dtor but no onDestroy was set
After reassigning b1
Here the program works with b1...
TestFunction: the A that was created inside getSomeA
实际问题:第一次破坏对象在哪里,以便调用testFunction?在getSomeA()
或在赋值之前但在getSomeA()
中创建对象之后的主函数中?
所有修改:我试图将问题缩短一小时,但由于我不了解C ++中的移动/复制语义,这对我来说很难。
解决方法
在第一个版本中,作为返回getSomeA
中构造的对象的一部分,对象首次被销毁。它的return
语句有效地构造了一个临时对象,然后将其分配给main
的{{1}}。如果您忽略从函数返回的过程,则事件的顺序为:
b1
这时,由于临时对象被销毁,因此绑定函数被调用。临时对象是一个完全被欺骗的对象,具有所有权利和特权。包括析构函数。
但是,等等,还有更多! A <temporary object>(std::bind( ... )
b1=<temporary object>
<temporary object gets destroyed>
是原始对象的完美逐位副本,具有绑定功能,可以被销毁。因此,b1
被销毁后,该函数将再次被调用。
这是Rule Of Three的间接结果。当对象拥有资源,并且必须维护该资源的排他性所有权时,您将需要提供副本和/或移动构造函数 和 和赋值运算符以准确说明在那种情况下应该发生什么。
P.S。这些规则随着C ++ 17保证的复制省略而略有变化,但是基本概念仍然相同。