移动构造函数相对于采用 bool 表示是复制还是移动的复制构造函数有什么优势?
What is the advantage of a move constructor over a copy constructor that takes a bool that says whether to copy or move?
为什么我们需要 C++ 中的移动 constructor/assignment 运算符,而我们只能这样做:
Foo(const Foo& x, bool copy = false) {
if (copy) {
// copy
}
else {
// move
}
}
还是我遗漏了什么?
类 在 C++ 中可以表示任何东西。文件、线程、字符串、3d 对象 - 所有这些都可以表示为某些 class 的实例。这些对象可能有很多内部数据。考虑以下示例:
class Image
{
protected:
Ubyte* _data;
Size _size;
PixelFormat _pixel_format;
public:
Image()
: _data(nullptr)
, _size(0,0)
, _pixel_format(PixelFormat::Unknown)
{
}
Image(Image&& source)
: _data(nullptr)
, _size(0,0)
, _pixel_format(PixelFormat::Unknown)
{
this->Swap(source);
}
Image(const Image& origin)
: _data(nullptr)
, _size(0,0)
, _pixel_format(PixelFormat::Unknown)
{
this->InitWithDeepCopyOf(origin);
}
void Swap(Image& other)
{
std::swap(this->_data, other._data);
std::swap(this->_size, other._size);
std::swap(this->_pixel_format, other._pixel_format);
}
};
InitWithDeepCopyOf(const Image& img) 使用存储在 img 中的数据初始化图像,但它首先完整复制该数据(考虑具有全高清分辨率的 32bpp 图像 - InitWithDeepCopyOf() 将复制 8.3MB 的数据!)。
在我们的程序中,还有一个class:
class ImageHolder
{
protected:
Image _image; //stores image by value
public:
ImageHolder()
{
}
ImageHolder(ImageHolder&& source)
: _image(std::move(source._image))
{
}
ImageHolder(const ImageHolder& origin)
: _image(source._image)
{
}
};
在ImageHolder
的move ctor中,我们简单的move构造了_image
成员,所以它将默认初始化Image
和3次交换。复制构造函数需要复制构造_image
,所以所有数据都会被复制。
让我们考虑使用 bool
标志的解决方案。它有 2 个问题:
1)我们做不到。
ImageHolder(const ImageHolder& origin, bool copy = true)
: _image(source._image)
{
if(copy)
//...
else
//...
}
如果 origin
声明为 const ImageHolder&,我们不能移动它,因为移动需要修改源对象 - 而我们不能修改 const 对象。
如果 origin
声明为非常量 ImageHolder&,我们将无法执行此操作:
ImageHolder new_holder(ImageHolder(image)); //parameter is a const reference
2) 在构造函数体中,成员已经被构造。
当我们到达此代码时:
if(copy)
//...
else
//...
_image
已经构造(更准确地说:复制构造),这意味着您已经复制了所有数据(因此没有必要移动任何东西)。
所以,是的,一切都与性能有关。如果您认为,您可以将 CPU 时间浪费在不必要地复制数百万字节的数据上...毕竟,这仅与性能有关。
移动构造函数是为您隐式编写的(除非您阻止它)。
在某些情况下会自动为您调用移动构造函数,即使您在它们存在之前编写了代码。
仅移动类型可以与移动构造函数一起存在,并且它们会在编译时阻止复制操作并出错。
将某些内容标记为 'please move from this' 不需要第二个参数,可以完美转发。完美转发也只适用于右值。 (完美的转发是不完美的,顺便说一句)
移动分配不适用于您的模式。
右值引用在 move/assign 之外的上下文中很有用。
老实说,将 C++11 移动和右值引用与您的提案进行比较就像在问为什么 Telsa 比车轮坏了的三轮车更好。破三轮车更便宜,我给。
为什么我们需要 C++ 中的移动 constructor/assignment 运算符,而我们只能这样做:
Foo(const Foo& x, bool copy = false) {
if (copy) {
// copy
}
else {
// move
}
}
还是我遗漏了什么?
类 在 C++ 中可以表示任何东西。文件、线程、字符串、3d 对象 - 所有这些都可以表示为某些 class 的实例。这些对象可能有很多内部数据。考虑以下示例:
class Image
{
protected:
Ubyte* _data;
Size _size;
PixelFormat _pixel_format;
public:
Image()
: _data(nullptr)
, _size(0,0)
, _pixel_format(PixelFormat::Unknown)
{
}
Image(Image&& source)
: _data(nullptr)
, _size(0,0)
, _pixel_format(PixelFormat::Unknown)
{
this->Swap(source);
}
Image(const Image& origin)
: _data(nullptr)
, _size(0,0)
, _pixel_format(PixelFormat::Unknown)
{
this->InitWithDeepCopyOf(origin);
}
void Swap(Image& other)
{
std::swap(this->_data, other._data);
std::swap(this->_size, other._size);
std::swap(this->_pixel_format, other._pixel_format);
}
};
InitWithDeepCopyOf(const Image& img) 使用存储在 img 中的数据初始化图像,但它首先完整复制该数据(考虑具有全高清分辨率的 32bpp 图像 - InitWithDeepCopyOf() 将复制 8.3MB 的数据!)。
在我们的程序中,还有一个class:
class ImageHolder
{
protected:
Image _image; //stores image by value
public:
ImageHolder()
{
}
ImageHolder(ImageHolder&& source)
: _image(std::move(source._image))
{
}
ImageHolder(const ImageHolder& origin)
: _image(source._image)
{
}
};
在ImageHolder
的move ctor中,我们简单的move构造了_image
成员,所以它将默认初始化Image
和3次交换。复制构造函数需要复制构造_image
,所以所有数据都会被复制。
让我们考虑使用 bool
标志的解决方案。它有 2 个问题:
1)我们做不到。
ImageHolder(const ImageHolder& origin, bool copy = true)
: _image(source._image)
{
if(copy)
//...
else
//...
}
如果 origin
声明为 const ImageHolder&,我们不能移动它,因为移动需要修改源对象 - 而我们不能修改 const 对象。
如果 origin
声明为非常量 ImageHolder&,我们将无法执行此操作:
ImageHolder new_holder(ImageHolder(image)); //parameter is a const reference
2) 在构造函数体中,成员已经被构造。
当我们到达此代码时:
if(copy)
//...
else
//...
_image
已经构造(更准确地说:复制构造),这意味着您已经复制了所有数据(因此没有必要移动任何东西)。
所以,是的,一切都与性能有关。如果您认为,您可以将 CPU 时间浪费在不必要地复制数百万字节的数据上...毕竟,这仅与性能有关。
移动构造函数是为您隐式编写的(除非您阻止它)。
在某些情况下会自动为您调用移动构造函数,即使您在它们存在之前编写了代码。
仅移动类型可以与移动构造函数一起存在,并且它们会在编译时阻止复制操作并出错。
将某些内容标记为 'please move from this' 不需要第二个参数,可以完美转发。完美转发也只适用于右值。 (完美的转发是不完美的,顺便说一句)
移动分配不适用于您的模式。
右值引用在 move/assign 之外的上下文中很有用。
老实说,将 C++11 移动和右值引用与您的提案进行比较就像在问为什么 Telsa 比车轮坏了的三轮车更好。破三轮车更便宜,我给。