gpt4 book ai didi

c++ - 我应该删除 move 构造函数和智能指针的 move 分配吗?

转载 作者:IT老高 更新时间:2023-10-28 21:54:17 25 4
gpt4 key购买 nike

我正在实现一个简单的智能指针,它基本上跟踪对它处理的指针的引用数。

我知道我可以实现 move 语义,但我认为这没有意义,因为复制智能指针非常便宜。特别是考虑到它带来了产生讨厌的错误的机会。

这是我的 C++11 代码(我省略了一些无关紧要的代码)。也欢迎提出一般性意见。

#ifndef SMART_PTR_H_
#define SMART_PTR_H_

#include <cstdint>

template<typename T>
class SmartPtr {
private:
struct Ptr {
T* p_;
uint64_t count_;
Ptr(T* p) : p_{p}, count_{1} {}
~Ptr() { delete p_; }
};
public:
SmartPtr(T* p) : ptr_{new Ptr{p}} {}
~SmartPtr();

SmartPtr(const SmartPtr<T>& rhs);
SmartPtr(SmartPtr<T>&& rhs) =delete;

SmartPtr<T>& operator=(const SmartPtr<T>& rhs);
SmartPtr<T>& operator=(SmartPtr<T>&& rhs) =delete;

T& operator*() { return *ptr_->p_; }
T* operator->() { return ptr_->p_; }

uint64_t Count() const { return ptr_->count_; }

const T* Raw() const { return ptr_->p_; }
private:
Ptr* ptr_;
};

template<typename T>
SmartPtr<T>::~SmartPtr() {
if (!--ptr_->count_) {
delete ptr_;
}
ptr_ = nullptr;
}

template<typename T>
SmartPtr<T>::SmartPtr(const SmartPtr<T>& rhs) : ptr_{rhs.ptr_} {
++ptr_->count_;
}

template<typename T>
SmartPtr<T>& SmartPtr<T>::operator=(const SmartPtr<T>& rhs) {
if (this != &rhs) {
if (!--ptr_->count_) {
delete ptr_;
}
ptr_ = rhs.ptr_;
++ptr_->count_;
}
return *this;
}

#endif // SMART_PTR_H_

最佳答案

指南

永远不要删除必杀技成员。

在典型代码中(例如在您的问题中),删除 move 成员有两个动机。其中一个动机会产生不正确的代码(如您的示例),而另一个动机是删除 move 成员是多余的(既无害也无好处)。

  1. 如果你有一个可复制的类并且你不想 move 成员,那就不要声明它们(包括不删除它们)。仍会声明已删除的成员。删除的成员参与重载决议。不在场的成员没有。当您使用有效的复制构造函数和已删除的 move 成员创建类时,您不能从函数中按值返回它,因为重载决议将绑定(bind)到已删除的 move 成员。

  2. 有时人们想说:这个类既不可 move 也不可复制。删除拷贝和 move 成员是正确的。但是,只需删除复制成员就足够了(只要未声明 move 成员)。声明(甚至删除)复制成员禁止编译器声明 move 成员。所以在这种情况下,被删除的 move 成员只是多余的。

如果你声明删除的 move 成员,即使你碰巧选择了它是多余的而不是不正确的情况,每次有人阅读你的代码时,他们都需要重新发现你的情况是多余的还是不正确的。让您的代码阅读者更轻松,并且永远不要删除 move 成员。

不正确的情况:

struct CopyableButNotMovble
{
// ...
CopyableButNotMovble(const CopyableButNotMovble&);
CopyableButNotMovble& operator=(const CopyableButNotMovble&);
CopyableButNotMovble(CopyableButNotMovble&&) = delete;
CopyableButNotMovble& operator=(CopyableButNotMovble&&) = delete;
// ...
};

这是您可能希望使用 CopyableButNotMovble 但在编译时会失败的示例代码:

#include <algorithm>
#include <vector>

struct CopyableButNotMovble
{
// ...
CopyableButNotMovble(const CopyableButNotMovble&);
CopyableButNotMovble& operator=(const CopyableButNotMovble&);
CopyableButNotMovble(CopyableButNotMovble&&) = delete;
CopyableButNotMovble& operator=(CopyableButNotMovble&&) = delete;

CopyableButNotMovble(int);
// ...
friend bool operator<(CopyableButNotMovble const& x, CopyableButNotMovble const& y);
};

int
main()
{
std::vector<CopyableButNotMovble> v{3, 2, 1};
std::sort(v.begin(), v.end());
}

In file included from test.cpp:1:
algorithm:3932:17: error: no
matching function for call to 'swap'
swap(*__first, *__last);
^~~~
algorithm:4117:5: note: in
instantiation of function template specialization 'std::__1::__sort<std::__1::__less<CopyableButNotMovble,
CopyableButNotMovble> &, CopyableButNotMovble *>' requested here
__sort<_Comp_ref>(__first, __last, __comp);
^
algorithm:4126:12: note: in
instantiation of function template specialization 'std::__1::sort<CopyableButNotMovble *,
std::__1::__less<CopyableButNotMovble, CopyableButNotMovble> >' requested here
_VSTD::sort(__first, __last, __less<typename iterator_traits<_RandomAccessIterator>::value_type>());
^
...

(来自 std::lib 深处的许多讨厌的错误消息)

正确的做法是:

struct CopyableButNotMovble
{
// ...
CopyableButNotMovble(const CopyableButNotMovble&);
CopyableButNotMovble& operator=(const CopyableButNotMovble&);
// ...
};

冗余案例:

struct NeitherCopyableNorMovble
{
// ...
NeitherCopyableNorMovble(const NeitherCopyableNorMovble&) = delete;
NeitherCopyableNorMovble& operator=(const NeitherCopyableNorMovble&) = delete;
NeitherCopyableNorMovble(NeitherCopyableNorMovble&&) = delete;
NeitherCopyableNorMovble& operator=(NeitherCopyableNorMovble&&) = delete;
// ...
};

更易读的方法是:

struct NeitherCopyableNorMovble
{
// ...
NeitherCopyableNorMovble(const NeitherCopyableNorMovble&) = delete;
NeitherCopyableNorMovble& operator=(const NeitherCopyableNorMovble&) = delete;
// ...
};

如果您始终按照相同的顺序将所有 6 个特殊成员分组在类声明的顶部附近,并跳过您不想声明的那些,这将很有帮助。这种做法使您的代码的读者更容易快速确定您没有故意声明任何特定的特殊成员。

例如,这是我遵循的模式:

class X
{
// data members:

public:
// special members
~X();
X();
X(const X&);
X& operator=(const X&);
X(X&&);
X& operator=(X&&);

// Constructors
// ...
};

Here is a more in-depth explanation of this declaration style.

关于c++ - 我应该删除 move 构造函数和智能指针的 move 分配吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37092864/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com