C++ 规则 5 复制和移动(构造函数和赋值)警告:复制或移动
C++ Rule of 5 copy and move (constructor and assignment) caveat: to copy or move
我正在编写符合 c++11+ 标准的 class,是时候实施规则 5 了。
我有一个关于 copy/move constructors/assignment 的问题。据我了解,副本 constructor/assignment 应该复制您的 class(浅,深?)。如果您的 class 具有独特的成员,例如 unique_ptr,我预见到两种情况。
深拷贝对象
我不确定,就我而言,我将如何进行深度复制(参见下面的代码)。
将对象移动到另一个class
在我看来,在复制构造函数中移动指针会对用户产生意想不到的副作用,因为他们期待的是复制,而不是移动,并且被复制的原始对象将不再起作用。
复制也可能有问题,但是,就我而言,curl 对象可能包含敏感信息,例如 cookie 或密码?
使用这些约束为 classes 创建复制和移动 constructors/assignment 的实际方法是什么?要深拷贝,要移动,还是不显式和隐式定义一个拷贝构造函数(does the delete keyword do this)?
// client.h
#pragma once
#include <Poco/URI.h>
#include <curl/curl.h>
class client
{
public:
typedef Poco::URI uri_type;
// Constructor
client(const uri_type & auth);
// Destructor
virtual ~client();
// Copy constructor
client(const client & other);
// Move constructor
client(client && other);
// Copy assignment
client & operator=(const client & other);
// Move assignment operator
client & operator=(client && other);
private:
uri_type auth_;
// ... other variables (both unique and copyable) ommitted for simplicity.
std::unique_ptr<CURL, void(*)(CURL*)> ptr_curl_;
};
// client.cpp
#include <memory>
#include <Poco/URI.h>
#include <curl/curl.h>
#include "client.h"
// Constructor
client::client(const uri_type & auth)
: auth_(auth)
, ptr_curl_(curl_easy_init(), curl_easy_cleanup)
{
curl_global_init(CURL_GLOBAL_DEFAULT);
}
// Destructor
client::~client()
{
curl_global_cleanup();
}
// Copy constructor
client::client(const client & other)
{
// ... deep copy? move?
// how would you deep copy a unique_ptr<CURL>?
}
// Move constructor
client::client(client && other)
{
std::swap(*this, other);
}
// Copy assignment
client & client::operator=(const client & other)
{
// Cant get this to work by making the copy happen in the parameter.
client temp(other);
std::swap(*this, temp);
return *this;
}
// Move assignment operator
client & client::operator=(client && other)
{
return *this;
}
顾名思义,复制 constructor/assignment 运算符应始终复制而不移动其成员,其中复制通常表示深复制。
记住:默认情况下,C++ 中的所有对象都应该具有值语义,即它们的行为应该像 int
。
此外,您 post 中的术语表明您将唯一对象(单例)与 unique_ptr
指向的对象混淆了。大多数不可复制的对象都是处理程序(例如 unique_ptr 处理堆上的对象),在这种情况下,您将复制它们处理的任何内容。如果那不可能,那么很可能实现对象的复制构造函数根本没有意义。
如果您的对象拥有对唯一资源的引用(您的项目中只能有一个实例),那么第一个问题是:它可以共享吗? -> 使用 shared_ptr。如果不是 -> 不要复制。
如果您的对象持有对唯一资源(原始指针或引用)的非拥有引用,请复制该引用。在这两种情况下,请注意您现在有两个共享其部分状态的对象,即使在非多线程应用程序中也可能很危险。
对于某些类型,拥有复制构造函数是不合适的。这包括大多数在语义上包含不可复制类型的类型。 (不计算 unique_ptr
,计算 CURL 实例)
如果 libcurl 具有 "duplicate_handle" 类函数,那么您的复制构造函数应该使用它来初始化副本中的 unique_ptr。否则,你应该删除你的复制构造函数并只实现移动。
我正在编写符合 c++11+ 标准的 class,是时候实施规则 5 了。
我有一个关于 copy/move constructors/assignment 的问题。据我了解,副本 constructor/assignment 应该复制您的 class(浅,深?)。如果您的 class 具有独特的成员,例如 unique_ptr,我预见到两种情况。
深拷贝对象
我不确定,就我而言,我将如何进行深度复制(参见下面的代码)。
将对象移动到另一个class
在我看来,在复制构造函数中移动指针会对用户产生意想不到的副作用,因为他们期待的是复制,而不是移动,并且被复制的原始对象将不再起作用。
复制也可能有问题,但是,就我而言,curl 对象可能包含敏感信息,例如 cookie 或密码?
使用这些约束为 classes 创建复制和移动 constructors/assignment 的实际方法是什么?要深拷贝,要移动,还是不显式和隐式定义一个拷贝构造函数(does the delete keyword do this)?
// client.h
#pragma once
#include <Poco/URI.h>
#include <curl/curl.h>
class client
{
public:
typedef Poco::URI uri_type;
// Constructor
client(const uri_type & auth);
// Destructor
virtual ~client();
// Copy constructor
client(const client & other);
// Move constructor
client(client && other);
// Copy assignment
client & operator=(const client & other);
// Move assignment operator
client & operator=(client && other);
private:
uri_type auth_;
// ... other variables (both unique and copyable) ommitted for simplicity.
std::unique_ptr<CURL, void(*)(CURL*)> ptr_curl_;
};
// client.cpp
#include <memory>
#include <Poco/URI.h>
#include <curl/curl.h>
#include "client.h"
// Constructor
client::client(const uri_type & auth)
: auth_(auth)
, ptr_curl_(curl_easy_init(), curl_easy_cleanup)
{
curl_global_init(CURL_GLOBAL_DEFAULT);
}
// Destructor
client::~client()
{
curl_global_cleanup();
}
// Copy constructor
client::client(const client & other)
{
// ... deep copy? move?
// how would you deep copy a unique_ptr<CURL>?
}
// Move constructor
client::client(client && other)
{
std::swap(*this, other);
}
// Copy assignment
client & client::operator=(const client & other)
{
// Cant get this to work by making the copy happen in the parameter.
client temp(other);
std::swap(*this, temp);
return *this;
}
// Move assignment operator
client & client::operator=(client && other)
{
return *this;
}
顾名思义,复制 constructor/assignment 运算符应始终复制而不移动其成员,其中复制通常表示深复制。
记住:默认情况下,C++ 中的所有对象都应该具有值语义,即它们的行为应该像 int
。
此外,您 post 中的术语表明您将唯一对象(单例)与 unique_ptr
指向的对象混淆了。大多数不可复制的对象都是处理程序(例如 unique_ptr 处理堆上的对象),在这种情况下,您将复制它们处理的任何内容。如果那不可能,那么很可能实现对象的复制构造函数根本没有意义。
如果您的对象拥有对唯一资源的引用(您的项目中只能有一个实例),那么第一个问题是:它可以共享吗? -> 使用 shared_ptr。如果不是 -> 不要复制。 如果您的对象持有对唯一资源(原始指针或引用)的非拥有引用,请复制该引用。在这两种情况下,请注意您现在有两个共享其部分状态的对象,即使在非多线程应用程序中也可能很危险。
对于某些类型,拥有复制构造函数是不合适的。这包括大多数在语义上包含不可复制类型的类型。 (不计算 unique_ptr
,计算 CURL 实例)
如果 libcurl 具有 "duplicate_handle" 类函数,那么您的复制构造函数应该使用它来初始化副本中的 unique_ptr。否则,你应该删除你的复制构造函数并只实现移动。