具有unique_ptr成员变量的 C++ 移动/复制/赋值<AbstractClass>
c++ move/copy/assignment with a unique_ptr<AbstractClass> member variable
我遇到了一个与非常相似的问题
如何使具有unique_ptr成员的类使用std::move和std::swap?
并且我正在使用VS2013,所以我希望以下内容能够起作用。
#include <memory>
#include <string>
class AbstractCamera{
public:
AbstractCamera(const std::string& name) :_name(name){}
virtual void method() = 0;
std::string _name;
};
class CameraImpl : public AbstractCamera{
public:
CameraImpl() :AbstractCamera("CameraImpl"){}
void method(){}
};
class RenderManager{
public:
RenderManager():_currentCamera(std::move(std::make_unique<CameraImpl>())){}
private:
std::unique_ptr<AbstractCamera> _currentCamera;
};
class Engine{
public:
Engine(){}
private:
RenderManager r;
};
int main(){
Engine e;
e = Engine(); // Causes error: C2280 call of deleted function
}
path\engine.cpp(75):错误C2280:"std::unique_ptr>&std::unique_ptr<_Ty,std::default_delete<_Ty>>::operator=(const std::unique_ptr<_Ty,std::default_delete<_Ty>>)':试图引用已删除的函数具有[_Ty=AbstractCamera]other_path\memory(1487):请参见'std::unique_ptr>::operator='的声明具有[_Ty=AbstractCamera]此诊断发生在编译器生成的函数"RenderManager&RenderManager::operator=(const RenderManager&)'
我确实得到了像这样的简单例子
std::unique_ptr<A> a = std::make_unique<A>();
std::unique_ptr<A> b = a;
是不允许的,但我的问题是以下代码:
Engine e;
e = Engine();
因为unique_ptr的赋值运算符被删除了,但这对类层次结构(如Engine->RenderMenger->unique_ptrmember)有何影响。
我确实知道,Engine e
使用默认构造函数Engine(),e = Engine()
调用默认构造函数和Engine的运算符=来将临时Engine对象分配给e。
因此,我的问题是:代码试图在哪里复制/分配unique_ptr,以及如何解决它?
我试图尽可能地删除原始代码,但无法使用更简单的示例和SSCCEE形式的表意符号来重现错误,因为我真的不明白是什么导致了问题,所以很抱歉!
VS2013不会自动生成移动特殊成员函数(如果不明显,这是不符合要求的)。您必须自己编写Engine
和RenderManager
的移动构造函数和移动赋值运算符,否则将使用复制特殊成员函数。
附带说明一下,_currentCamera(std::move(std::make_unique<CameraImpl>))
不会编译;CCD_ 6会,但CCD_。make_unique
已经返回了一个右值。代码中还有其他一些拼写错误,可能是由于最小化过程造成的。
T.C.正确回答了这个问题,但我想提供一些进一步的见解,因为我实际上问错了问题:)
因此,真正的问题应该是
我如何使用以下代码
Engine e;
e = Engine();
当我的意图是破坏默认构造的对象e并分配新构造的对象时。
首先,我们需要了解复制和移动赋值和构造之间的区别,下面的线程,尤其是@FredOverflow在这里的两篇长帖子,可以帮助我们理解这一点:什么是移动语义?
问题设置实际上是具有std::vectors<std::unique_ptr<T>>
成员的类和具有std::unique_ptr<T>
成员的类。看见http://www.cplusplus.com/forum/beginner/110610/以及JLJorges的回答作为一个更长的例子。
JLJorges发布:
所以不要做任何事情:默认情况下,B是不可复制或可分配的,但B是可移动和可移动的
JLJorges指出了在这种情况下的两件重要事情:
- 默认情况下,包含不可复制成员的类不可复制
- 默认情况下,包含不可复制但可移动/moveAssignable成员的类仍然可以是可移动/moveAssignable
因此,VS2013能够隐式生成move和move-assign运算符/构造函数,就像它为这样的类设置所做的那样。
我没有选择T.C.的答案,尽管他很有帮助,而且接近答案,原因如下:
如果需要可复制/可复制分配的类,则必须实现复制构造函数和副本分配。如果我需要最初问题中的代码:一个可复制/可复制的类Engine,那么这就是答案。
如果一个人实现了其中一个方法/运算符,他可能也想实现其他方法/运算符。请参阅以下链接了解解释:
- 用C++11,三条规则变成五条规则
- http://en.wikipedia.org/wiki/Rule_of_three_%28C%2B%2B_programming%29#Rule_of_5
正如我所说,我被问错了问题,因为我不需要一个可复制/可复制分配的类,所以解决方案应该是实现移动构造函数和移动分配运算符,或者像JLJorges指出的那样:什么都不做,使用隐式生成的版本,但当我想移动时,我显然必须告诉编译器:
Engine e;
e = std::move(Engine());
e = Engine()
调用Engine的移动分配运算符。由于您没有,所以将调用它的副本分配运算符。然后将调用RenderManger的副本分配。最后是unique_ptr的副本分配,它被删除了。
引擎需要移动分配,如
Engine & operator = (Engine && rhs) {
r._currentCamera = std::move(rhs.r._currentCamera);
return * this;
}
假设RenderManager::_currentCamera对Engine可见,否则您需要为RenderManager定义移动分配。
- CRTP 中的复制赋值运算符 - gcc vs clang 和 msvc
- 如何在双向链表上实现复制赋值?
- 复制赋值函数如何访问另一个对象的私有成员(Stroustroup 原则和实践书)?
- 为什么基类中的复制和交换会导致派生类中的复制赋值运算符被隐式删除?
- 在c++中重载复制赋值运算符
- 复制构造函数和复制赋值运算符是否应具有相同的语句?
- 移动赋值运算符与复制赋值运算符
- Gcc 使用 memcpy 作为隐式复制赋值运算符,而不是成员复制
- 为什么 GCC 拒绝复制赋值操作中的常量引用
- 复制赋值构造函数中的aligned_alloc内存块在释放时崩溃
- 复制赋值和复制构造函数(代码C++的差异)
- 如何在没有复制赋值运算符的情况下交换两个对象
- 对于具有抛出复制构造函数和noexcept-by-value复制赋值的类,is_nothrow_copy_assigna
- 模拟 C++ 中 lambda 的复制赋值运算符
- 重载复制赋值运算符
- 为什么C++编译器会创建复制构造函数和复制赋值运算符
- 为什么在进行复制赋值之前调用复制构造函数
- 为什么当复制赋值函数不返回任何内容时编译器不引发错误?
- 默认的复制构造函数和复制赋值运算符给出奇怪的错误
- 重载运算符 = 返回 void 是否不可能成为复制赋值运算符