C++ std::vector::swap实际上做什么?
引发这一问题的是以下代码:C++ std::vector::swap实际上做什么?,c++,vector,swap,C++,Vector,Swap,引发这一问题的是以下代码: std::vector<int> x(500); std::vector<int> y; std::swap(x,y); vector内部存储(至少)指向元素实际存储的指针、大小和容量。†std::swap仅交换指针、大小和容量(以及辅助数据,如有);由于x中的指针变为y中的指针,反之亦然,且未分配任何新内存,因此不会对内存或元素进行加倍 vector的迭代器通常是围绕指向底层分配内存的指针的轻量级包装器(这就是容量更改通常使迭代器无效的原
std::vector<int> x(500);
std::vector<int> y;
std::swap(x,y);
vector
内部存储(至少)指向元素实际存储的指针、大小和容量。†std::swap
仅交换指针、大小和容量(以及辅助数据,如有);由于x
中的指针变为y
中的指针,反之亦然,且未分配任何新内存,因此不会对内存或元素进行加倍
vector
的迭代器通常是围绕指向底层分配内存的指针的轻量级包装器(这就是容量更改通常使迭代器无效的原因);您使用的sort
示例是合法的,并且sortsy
如果您想在不交换存储的情况下交换元素本身(这是一个更为昂贵的操作,但它会为x
引用x
留下先前存在的迭代器),您可以这样做,但这是一个相对不常见的用例。其内存使用情况取决于底层对象的swap
实现;默认情况下,它通常会涉及一个临时变量,但一次只能交换一个对象,而不是整个向量
†根据评论,它可以等效地使用指向已用空间末端和容量末端的指针,但这两种方法在逻辑上都是等效的,只是为了稍微不同的预期用例进行了微优化;存储所有指针可以优化迭代器的使用(一个合理的选择),而存储
size\u type
可以优化.size()
/.capacity()
调用。vector
内部存储(至少)指向元素实际存储的指针,大小和容量。†std::swap
仅交换指针、大小和容量(以及辅助数据,如果有的话);由于x
中的指针变为y
中的指针,反之亦然,且未分配任何新内存,因此不会对内存或元素进行加倍
vector
的迭代器通常是围绕指向底层分配内存的指针的轻量级包装器(这就是容量更改通常使迭代器无效的原因);您使用的sort
示例是合法的,并且sortsy
如果您想在不交换存储的情况下交换元素本身(这是一个更为昂贵的操作,但它会为x
引用x
留下先前存在的迭代器),您可以这样做,但这是一个相对不常见的用例。其内存使用情况取决于底层对象的swap
实现;默认情况下,它通常会涉及一个临时变量,但一次只能交换一个对象,而不是整个向量
†根据评论,它可以等效地使用指向已用空间末端和容量末端的指针,但这两种方法在逻辑上都是等效的,只是为了稍微不同的预期用例进行了微优化;存储所有指针可以优化迭代器的使用(一个合理的选择),而存储
size\u type
可以优化.size()
/.capacity()
调用。我将编写一个玩具向量
struct toy_vector {
int * buffer = 0;
std::size_t valid_count = 0;
std::size_t buffer_size = 0;
int* begin() { return buffer; }
int* end() { return buffer+valid_count; }
std::size_t capacity() const { return buffer_size; }
std::size_t size() const { return valid_count; }
void swap( toy_vector& other ) {
std::swap( buffer, other.buffer );
std::swap( valid_count, other.valid_count );
std::swap( buffer_size, other.buffer_size );
}
基本上就是这样。我将实现一些方法,以便您看到我们有足够的工具来使用:
int& operator[](std::size_t i) { return buffer[i]; }
void reserve(int capacity) {
if (capacity <= buffer_size)
return;
toy_vector tmp;
tmp.buffer = new int[capacity];
for (std::size_t i = 0; i < valid_count; ++i)
tmp.buffer[i] = std::move(buffer[i]);
tmp.valid_count = valid_count;
tmp.buffer_size = capacity;
swap( tmp );
}
void push_back(int x) {
if (valid_count+1 > buffer_size) {
reserve( (std::max)((buffer_size*3/2), buffer_size+1) );
buffer[valid_count] = std::move(x);
++valid_count;
}
// real dtor is more complex.
~toy_vector() { delete[] buffer; }
};
int&运算符[](std::size_t i){返回缓冲区[i];}
空位储备(国际容量){
if(容量缓冲区大小){
保留((标准::最大值)((缓冲区大小*3/2),缓冲区大小+1));
缓冲区[有效计数]=标准::移动(x);
++有效计数;
}
//真正的dtor更复杂。
~toy_vector(){delete[]buffer;}
};
实际的向量存在异常安全问题,更关注对象生存期和分配器(我使用的是INT,所以不关心是否正确构造/销毁它们),并且可能存储3个指针而不是一个指针和2个大小。但是交换这3个指针就像交换指针和2个大小一样简单
实向量中的迭代器往往不是原始指针(但可以是)。如上所述,当您执行a.swap(b)
时,指向向量a
的原始指针迭代器变成指向向量b
的原始指针迭代器;非原始指针向量迭代器基本上是花哨的包装指针,并且必须遵循相同的语义
C++标准没有明确地指出“强>任务< /强>这样的实现,但是它是基于这样的实现而建立的,它隐含地要求一个与此几乎相同的实现。(我相信有人会想出一个聪明的标准兼容向量,它看起来不是这样的;但我见过的每个标准库中的每个向量都是这样的。)
我将编写一个玩具向量struct toy_vector {
int * buffer = 0;
std::size_t valid_count = 0;
std::size_t buffer_size = 0;
int* begin() { return buffer; }
int* end() { return buffer+valid_count; }
std::size_t capacity() const { return buffer_size; }
std::size_t size() const { return valid_count; }
void swap( toy_vector& other ) {
std::swap( buffer, other.buffer );
std::swap( valid_count, other.valid_count );
std::swap( buffer_size, other.buffer_size );
}
基本上就是这样。我将实现一些方法,以便您看到我们有足够的工具来使用:
int& operator[](std::size_t i) { return buffer[i]; }
void reserve(int capacity) {
if (capacity <= buffer_size)
return;
toy_vector tmp;
tmp.buffer = new int[capacity];
for (std::size_t i = 0; i < valid_count; ++i)
tmp.buffer[i] = std::move(buffer[i]);
tmp.valid_count = valid_count;
tmp.buffer_size = capacity;
swap( tmp );
}
void push_back(int x) {
if (valid_count+1 > buffer_size) {
reserve( (std::max)((buffer_size*3/2), buffer_size+1) );
buffer[valid_count] = std::move(x);
++valid_count;
}
// real dtor is more complex.
~toy_vector() { delete[] buffer; }
};
int&运算符[](std::size_t i){返回缓冲区[i];}
空位储备(国际容量){
if(容量缓冲区大小){
保留((标准::最大值)((缓冲区大小*3/2),缓冲区大小+1));
缓冲区[有效计数]=标准::移动(x);
++有效计数;
}
//真正的dtor更复杂。
~toy_vector(){delete[]buffer;}
};
实际的向量存在异常安全问题,更关注对象生命周期和分配器(我使用的是INT,所以不关心是否正确构造/销毁它们),可能存储3个指针,而不是一个指针和2个大小。但是交换这3个指针就像交换指针和2个大小一样简单
实向量中的迭代器往往不是原始指针(但它们可以是)。如上所述,当您执行a.swap(b)
;非原始指针向量iter时,指向向量a
的原始指针迭代器变成指向向量b
的原始指针迭代器