我正在尝试实现我自己的 vector 类(用于学习目的)。目前我卡在 vector::clear()
上,我该如何实现它?总不能这样吧?
void clear() {
for (size_t i = 0; i < _size; ++i) {
_data = 0;//can't work with custom class
}
}
或者
void clear() {
delete[] _data;
_data = new T[_cap];
_size = 0;
}//can we make it better?
或者像这样简单:
void clear() {
_size = 0;//fastest, but user can still access the data
}
T& operator[](size_t pos) {
if (pos >= _size) throw;//added to prevent from accessing "cleared" data
return _data[pos];
}
通过 libcxx 做了一些挖掘并发现他们使用 alloc_traits::destroy , 这会破坏 _data
中的每个元素???
这是我的类属性
T* _data;
size_t _size;
size_t _cap;
最佳答案
如果您保持容量大于大小,这意味着您需要考虑分配的不包含任何对象的内存。这意味着 new T[_cap]
方法根本不起作用:
- 首先也是最重要的是,您的 vector 不适用于默认不可构造的对象
- 即使是那些,您创建的对象也会比请求的多,而且对于某些对象而言,构建的成本可能很高。
- 另一个问题是,当你
push_back
时,当你仍然有能力时,你将对对象进行赋值而不是构造(因为对象已经存在)
因此您需要将内存分配与对象创建分离:
- 用
operator new
分配内存.请注意这不同于 new expression你最熟悉。 - 用in-place constructor 在分配的内存中构造一个对象(也称为展示位置新)
- 通过显式调用析构函数来销毁对象
- 使用
operator delete
释放内存
C++17 为此目的引入了一些实用函数,例如:std::uninitialized_default_construct
, uninitialized_copy
, std::destroy
ETC。;在 Dynamic memory management 中找到更多信息
如果你想像 std::vector
那样更通用,你可以使用 allocators相反。
考虑到这一点,现在回答您关于clear
的具体问题。 std::vector::clear
的行为是:“从容器中删除所有元素。此调用后,size()
返回零。[...] 保持 vector 的 capacity()
不变”。如果您想要相同的行为,这意味着:
void clear() noexcept
{
for (T* it = _data; it != _data + _size; ++it)
it->~T();
// or
std::destroy(_data, _data + size);
_size = 0;
}
如您所见,实现类似 std::vector
的东西绝非易事,需要一些专业知识和技术。
并发症的另一个来源来自 strong exception safety .让我们仅考虑 push_back
的情况作为示例。一个简单的实现可以做到这一点(伪代码):
void push_back(const T& obj)
{
if size == capacity
// grow capacity
new_data = allocate memory
move objects from _data to new_data
_data = new_data
update _cap
new (_data + _size) T{obj}; // in-place construct
++_size;
}
现在想想如果一个对象的移动构造函数在移动到新的更大的内存时抛出异常会发生什么。你有内存泄漏,最糟糕的是:你的 vector 中有一些对象处于移动状态。这将使您的 vector 处于无效的内部状态。这就是为什么 std::vector::push_back
保证:
- 运营商成功或
- 如果抛出异常,则该函数无效。
换句话说,它保证它永远不会让对象处于“中间”或无效状态,就像我们天真的实现所做的那样。
关于c++ - 如何实现 vector::clear()? (学习目的),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63276697/