7

当我学习 C++ 时,人们告诉我至少要实现三种方法的规则

现在我看到了新的“... = default;” 来自堆栈溢出的 c++0x,我的问题是:

是否为这些方法定义了 c++11 标准实现,还是特定于编译器?

另外我想有一些精度:

  • 就代码而言,实现是什么样的?(如果它是通用的)
  • 与下面的示例实现相比,这有优势吗?
  • 如果您不使用赋值/复制构造函数,*... = delete*具体做什么,将它们声明为私有有什么区别?答案(来自@40two)
  • 新的 default= 与旧的默认实现不同吗?

免责声明:当我的方法需要更高级的功能时,我肯定会自己实现它们。但是我习惯于实现赋值运算符和复制构造函数,即使我从未使用过它们,只是为了让编译器不使用它们。


我曾经做过的事情:(编辑,@DDrmmr swap/move

//File T.h
class T
{
  public:
    T(void);
    T(const T &other);
    T(const T &&other);
    T &operator=(T other);
    friend void swap(T &first, T &second);
    ~T(void);

  protected:
    int *_param;
};

//File T.cpp
T::T(void) :
  _param(std::null)
{}

T::T(T &other)
  : _param(other._param)
{}

T::T(T &&other)
  : T()
{
  swap(*this, other);
}

T &T::operator=(T other)
{
  swap(*this, other);
  return (*this);
}

friend void swap(T &first, T &second)
{
  using std::swap;

  swap(first._param, second._param);
}

T::~T(void)
{}
4

1 回答 1

4

默认行为是:

  • 默认 ctor ( T()):调用基类 def。ctors 和 members 默认 ctors。
  • Copy ctor ( T(const T&)): 调用bases copy。ctors 和成员复制 ctors。
  • 移动 ctor ( T(T&&)):调用基地移动。ctors 和 members 移动 ctors。
  • 分配(T& operator=(const T&)):调用基地分配。和成员分配。
  • 转接(T& operator=(T&&)):呼叫基地转接,成员转接。
  • 析构函数( ~T()):调用成员析构函数,基析构函数(倒序)。

对于内置类型(int 等)

  • 默认 ctor:如果显式调用,则设置为 0
  • 复制ctor:按位复制
  • 移动ctor:按位复制(源没有变化)
  • 赋值:按位复制
  • 传输:按位复制
  • 析构函数:什么都不做。

由于指针也是内置类型,因此这适用于int*(而不是它指向的内容)。

现在,如果您不声明任何内容,您的T类将只保存一个不拥有指向 int 的 int*,因此 T 的副本将只保存指向同一个 int 的指针。这与 C++03 产生的行为相同。内置类型的默认实现移动是复制。对于类是成员移动(并且取决于成员是什么:只是内置的副本)

如果你必须改变这种行为,你必须连贯地去做:例如,如果你想“拥有”你所指向的东西,你需要

  • 一个默认的 ctor 初始化为nullptr:这定义了一个“空状态”,我们稍后可以参考
  • 初始化到给定指针的创建者 ctor
  • 初始化为指向的副本的副本 ctor(这是真正的更改)
  • 删除指向的 dtor
  • 删除指向并接收指向的新副本的分配

.

T::T() :_param() {}
T::T(int* s) :_param(s) {}
T(const T& s) :_param(s._param? new int(*s._param): nullptr) {}
~T() { delete _param; } // will do nothing if _param is nullptr

现在让我们不要定义分配,而是专注于移动:如果你不声明它,因为你声明了副本,它将被删除:这使得 T 对象即使是临时的也总是被复制(与 c 相同的行为++03)

但是如果源对象是临时的,我们可以创建一个空的目标并交换它们:

T::T(T&& s) :T() { std::swap(_param, s._param); }

这就是所谓的一

现在分配:在 C++11 T& operator=(const T& s)应该检查自分配之前,使目标为空并接收指向的副本:

T& operator=(const T& s)
{
    if(this == &s) return *this; // we can shortcut
    int* p = new int(s._param); //get the copy ...
    delete _param; //.. and if succeeded (no exception while copying) ...
    _param = p; // ... delete the old and keep the copy
    return *this;
}

使用 C++11,我们可以使用参数传递来生成副本,从而给出

T& operator=(T s) //note the signature
{ std::swap(_param, s._param); return *this; }

s请注意,这也适用于 C++98,但如果是临时的,则传递副本不会在传递移动中优化。这使得这种实现在 C++98 和 C++03 中没有利润,但在 C++11 中非常方便。

另请注意,无需专门std::swap针对 T:std::swap(a,b);将起作用,被实施为三个动作(不是复制)

实现交换函数的实践源于 T 有许多成员的情况,在移动和分配中都需要交换。但它可以是一个普通的私有成员函数。

于 2014-06-27T09:18:11.300 回答