为什么移动后observer_ptr不归零?
Why isn't observer_ptr zeroed after a move?
为什么移动操作后observer_ptr
不归零?
在其默认构造中正确地设置为nullptr
,这确实是有意义的(并且防止指向垃圾)。
因此,当std::move()
'd时,与std::string
, std::vector
等一样,应归零。
这将使它在一些有意义的原始指针的上下文中成为一个很好的候选者,以及自动在具有原始指针数据成员的类上生成移动操作,就像本例中一样。
编辑
正如@JonathanWakely在评论中指出的(这与前面提到的问题有关):
如果
observer_ptr
在移动后为null,则可以使用它来实现零规则适用于具有指针成员的类型。这是一个非常有用的功能。
似乎很多人一开始就忽略了这个想法的重点和实用性。
考虑:
template<typename Mutex>
class unique_lock
{
Mutex* pm;
public:
unique_lock() : pm() { }
unique_lock(Mutex& m) : pm(&m) { }
~unique_lock() { if (pm) pm->unlock(); }
unique_lock(unique_lock&& ul) : pm(ul.pm) { ul.pm = nullptr; }
unique_lock& operator=(unique_lock&& ul)
{
unique_lock(std::move(ul)).swap(*this);
return *this;
}
void swap(unique_lock& ul) { std::swap(pm, ul.pm); }
};
使用默认构造为空且移动后为空的"哑"智能指针,您可以默认三个特殊成员函数,因此它变成:
template<typename Mutex>
class unique_lock
{
tidy_ptr<Mutex> pm;
public:
unique_lock() = default; // 1
unique_lock(Mutex& m) : pm(&m) { }
~unique_lock() { if (pm) pm->unlock(); }
unique_lock(unique_lock&& ul) = default; // 2
unique_lock& operator=(unique_lock&& ul) = default; // 3
void swap(unique_lock& ul) { std::swap(pm, ul.pm); }
};
这就是为什么有一个哑的、不拥有的、移动后为空的智能指针很有用,比如tidy_ptr
但是observer_ptr
只是默认为空的构造,所以如果它是标准化的,它将有助于声明一个函数接受一个非拥有的指针,但它不会对像上面这样的类有用,所以我仍然需要另一个非拥有的哑指针类型。拥有两个不拥有的愚蠢智能指针类型似乎比没有更糟糕!
因此,move构造函数的设计是为了在某些情况下降低复制构造函数的成本。
让我们写出我们期望这些构造函数和析构函数是什么:(这有点简化,但对于本例来说很好)。
observer_ptr() {
this->ptr == nullptr;
}
observer_ptr(T *obj) {
this->ptr = obj;
}
observer_ptr(observer_ptr<T> const & obj) {
this->ptr = obj.ptr;
}
~observer_ptr() {
}
你建议类提供一个移动构造函数,看起来像:
observer_ptr(observer_ptr<T> && obj) {
this->ptr = obj.ptr;
obj.ptr = null;
}
当我建议现有的复制构造函数可以正常工作,并且比建议的移动构造函数便宜时。
那么std::vector
呢?
std::vector对象在被复制时,实际上是复制它所返回的数组。因此,std::vector复制构造函数看起来像:
vector(vector<T> const & obj) {
for (auto const & elem : obj)
this->push_back(elem);
}
std::vector的move构造函数可以优化这一点。它可以这样做,因为内存可以从obj
窃取。在std::vector中,这样做实际上很有用。
vector(vector<T> && obj) {
this->data_ptr = obj.data_ptr;
obj.data_ptr = nullptr;
obj.size = 0;
}
除了将移出的observer_ptr
归零对性能的影响较小之外,这很容易解决(复制而不是移动),主要原因可能是尽可能地模仿常规指针的行为,遵循最小意外原则。
然而,有一个潜在的更重要的性能问题:默认的move函数允许observer_ptr
是可复制的。普通可复制性允许使用std::memcpy
复制对象。如果observer_ptr
不是可复制的,那么具有observer_ptr
数据成员的任何类也不会,这会导致性能损失,并向下级联到组合类层次结构。
我不知道使用std::memcpy
优化可以获得什么样的性能改进,但它们可能比前面提到的小性能问题更重要。
- 有没有一种快速的方法可以将类的所有静态成员归零?
- 我可以说服自动生成的构造函数将我的 POD 类成员归零吗?
- 当函数耗尽时结构被归零?
- 将结构的所有元素归零的最佳方法
- std::map<whatever,double>会自动将值归零吗?
- 我是否需要手动将 PCREDENTIAL 归零.凭据blob
- 在C++中对二维数组进行归零.循环需要2个吗
- 是否应始终在析构函数中清除/归零成员数据
- 当我的编译器不符合标准时,如何将数组成员归零
- 我怎样才能只将类的填充字节归零
- 如何在 O(1) 中将数组归零
- 如何将新数组归零
- 如何使用opencv库将3D矩阵的元素归零
- 将mmap偏移量的最后12位归零
- 指向"raw"资源的指针是否应该在析构函数中归零?
- 将现有数组归零的最快方法是什么?
- 将多维数组的一部分归零
- 为什么将结构归零会导致对虚基类成员的访问丢失?
- 这是将数组归零最快的方法
- 使用memset对派生结构体进行归零