【发布时间】:2015-04-06 04:48:52
【问题描述】:
我正在编写一个符合 c++11+ 标准的类,是时候实现 5 规则了。
我有一个关于复制/移动构造函数/赋值的问题。我的理解是复制构造函数/赋值应该复制你的类(浅的,深的?)。如果您的类具有唯一成员(例如 unique_ptr),我可以预见两种情况。
-
制作对象的深层副本
就我而言,我不确定如何制作深层副本(请参阅下面的代码)。
-
将对象移动到其他类
在我看来,在复制构造函数中移动指针会给用户带来意想不到的副作用,因为他们期望的是复制而不是移动,并且被复制的原始对象将不再起作用。
复制也可能有问题,但是,就我而言,curl 对象可能包含敏感信息,例如 cookie 或密码?
为具有这些约束的类创建复制和移动构造函数/赋值的实际方法是什么?要深拷贝、移动还是不显式和隐式定义复制构造函数 (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;
}
【问题讨论】:
-
作为一个指导原则,当不确定是否支持移动或复制这样一些更复杂的情况时,首先让你的类不可复制,然后让客户端代码的需求成为更清晰 - 驱动您是否添加对复制和/或移动的支持,以及使用什么确切的语义,即浅与深,重新建立连接/cookie 等。如果您无法清楚地看到客户端需要工作什么,那就太早了完成设计。
-
@vsoftco 我不同意。遵循 0 规则的人让我必须在我想调试的任何时候为他们实现 5 种方法……如果你认为你不会通过调试来找出特定类的构建时间,那就再给你的职业生涯一些年。
-
@ChristopherPisz 在这种情况下,我只需实现一个跟踪对象的基类(即计数器、构造/销毁),然后在调试模式下从它派生(如
class MyClass #ifdef DEBUG : public Counter<MyClass> #endif),请参阅例如en.wikipedia.org/wiki/…。我仍然认为代码应该尽可能简单,并且应该尽可能多地重复使用。 -
@vsoftco 这似乎与保持它尽可能简单的想法背道而驰。您实际上是在编译器生成的代码之上引入更多代码来完成调试任务。请参阅此处的相关讨论:softwareengineering.stackexchange.com/questions/361392/…这周我一直在讨论与零相关的几个主题:)
标签: c++ move copy-constructor