写在前面:
1、使用范围for遍历容器的每个元素,需要使用auto& it引用元素的同一块内存空间,不要使用auto it因为范围for的原型就是容器的迭代器遍历元素,将迭代器所指向的元素值然后拷贝给it,所以it又是栈上的一块内存空间了。建议使用引用类型,若不需要修改则写成const auto &it,需要修改写出auto &it。
2、关于array的swap的两个数组的相同类型是指容器所装的元素类型一致,还要元素的个数相同。
一、swap操作交换两个相同类型的容器的内容,一般的容器(除array外),交换两个容器内容的操作会保证非常快,因为并没有交换元素本身,而只是交换了两个容器的内部数据结构。
注:这里交换两个容器的内部数据结构是指交换容器中各元素的内存地址,并不是交换各个元素变量所存储的值。除string外,swap使用之后指向容器的指针、引用和迭代器均有效(即开始指向那个元素,在swap之后这个指针、引用和迭代器依然指向该元素)。
测试代码如下:
void test1()
{
vector<int> v1 = { 1,2,3 };
vector<int> v2 = { 4,5,6,7,8,9 };
auto it1 = v1.begin();
auto it2 = v2.begin();
cout << "交换前迭代器it1指向的元素为:" << *it1 << endl;
cout << "交换前迭代器it2指向的元素为:" << *it2 << endl;
cout << "交换前v1每个元素的地址和值为:";
for (const auto &it : v1)
{
cout << &it << " " << it << " ";
}
cout << "\n交换前v2每个元素的地址和值为:";
for (const auto &it : v2)
{
cout << &it << " " << it << " ";
}
swap(v1, v2);
cout << endl << endl;
cout << "交换后迭代器it1指向的元素为:" << *it1 << endl;
cout << "交换后迭代器it2指向的元素为:" << *it2 << endl;
cout << "交换后v1每个元素的地址和值为:";
for (const auto &it : v1)
{
cout << &it << " " << it << " ";
}
cout << "\n交换后v2每个元素的地址和值为:";
for (const auto &it : v2)
{
cout << &it << " " << it << " ";
}
cout << endl;
auto it3=v1.begin();
cout<<"swap后的v1.begin()的值:";
cout<<*it3;
return 0;
}
运行结果如下:
可以看到,交换的是整个的内部数据结构,各元素原来所存储的值并没有发生改变,只是这些元素已经属于不用的容器了。所以容器中所存储的元素的内存地址也发生了改变。所以swap操作后,指向容器内部的迭代器、引用和指针都任然有效,原来绑定的是哪个元素现在绑定的还是哪个元素。
在交换后,新建一个auto it3=v1.begin()的值,就会是4.
二、而swap两个array则真正交换了各个元素的值:
测试代码如下:
void test2()
{
array<int,5> v1 = { 1,2,3,4,5 };
array<int,5> v2 = { 4,5,6,7,8 };
auto it1 = v1.begin();
auto it2 = v2.begin();
cout << "交换前迭代器it1指向的元素为:" << *it1 << endl;
cout << "交换前迭代器it2指向的元素为:" << *it2 << endl;
cout << "交换前v1每个元素的地址和值为:";
for (const auto &it : v1)
{
cout << &it << " " << it << " ";
}
cout << "\n交换前v2每个元素的地址和值为:";
for (const auto &it : v2)
{
cout << &it << " " << it << " ";
}
swap(v1, v2);
cout << endl << endl;
cout << "交换后迭代器it1指向的元素为:" << *it1 << endl;
cout << "交换后迭代器it2指向的元素为:" << *it2 << endl;
cout << "交换后v1每个元素的地址和值为:";
for (const auto &it : v1)
{
cout << &it << " " << it << " ";
}
cout << "\n交换后v2每个元素的地址和值为:";
for (const auto &it : v2)
{
cout << &it << " " << it << " ";
}
cout << endl;
auto it3=v1.begin();
cout<<"swap后的v1.begin()的值:";
cout<<*it3;
return 0;
}
运行结果如下:
可以看到,交换后,两个array交换了各个元素的值,即容器中所存的各个元素的内存地址并没有交换,只是交换了相应位置的元素的值,所以说swap两个array所需的时间和array中元素的数目成正比,同时,swap操作后,指向容器内部的迭代器、引用和指针都任然有效,原来绑定的是哪个元素现在绑定的还是哪个元素,只不过对应的元素值已经进行了交换。
所以新建的it3=v1.begin()和原来的v1.begin()都是4
三、在windows中:
和其它容器不同的是,对string调用swap会导致迭代器、引用和指针失效。因为string存储的是字符串,在string变量中真正存储字符串的是一个叫_Ptr的指针,它指向string所存储的字符串首地址,而字符串并没有固定地址,而是存储在一个临时内存区域中,所以当字符串发生改变时,会发生内存的重新分配,所以会导致迭代器、引用和指针失效。
测试代码:
void test3()
{
string s1{ "123" };
string s2{ "4567" };
auto it1 = s1.begin();
auto it2 = s2.begin();
auto &a=s1[1];
cout << "\n交换前s1每个元素的地址和值为:";
for (const auto &it : s1)
{
cout << &it << " " << it << " ";
}
cout << "\n交换前s2每个元素的地址和值为:";
for (const auto &it : s2)
{
cout << &it << " " << it << " ";
}
cout<<endl;
cout << "交换前迭代器it1指向的元素为:" <<&it1<<" "<<*it1 << endl;
cout << "交换前迭代器it2指向的元素为:" <<&it2<<" "<<*it2 << endl;
cout << "交换前迭代器a指向的元素为:" << a << endl;
cout<<s1.size();
swap(s1, s2);
// s1.swap(s2);
cout << endl << endl;
cout << "\n交换后s1每个元素的地址和值为:";
for (const auto &it : s1)
{
cout << &it << " " << it << " ";
}
cout << "\n交换后s2每个元素的地址和值为:";
for (const auto &it : s2)
{
cout << &it << " " << it << " ";
}
cout<<endl;
cout << "交换后迭代器it1指向的元素为:" <<&it1<< " "<<*it1 << endl;
cout << "交换后迭代器it2指向的元素为:" <<&it2<< " "<<*it2 << endl;
cout << "交换后迭代器a指向的元素为:" << a << endl;
return 0;
}
swap之前的各变量内存窗口:
swap之后的发生编译错误,显示迭代器已经失效了,效果图如下:
在Ubuntu中:
因为Ubuntu中C++底层是基于g++与微软中使用的MSVC底层不一样。
首先这里交换成功,表示这里string 的交换的是类似于Vector交换的是数据结构,而不是元素的值与array交换。
这里涉及到C++对string存储的一个短字符串优化(Short String Optimization,简称SSO)问题
首先看std::basic_string<CharT>
对象的最基本布局
struct {
CharT* ptr;
size_type size;
size_type capacity;
};
它的中 ptr
是指向大小为 capacity
的CharT
的动态分配的array的指针,另外它可以含有一个长度为 size
的字符串。倘若字符
串很短,单独为其开辟内存过于浪费,于是直接存储在string对象中。
std::string | std:: u16string | std::u32string | ||||
底层实现 | SSO容量 | sizeof | SSO容量 | sizeof | SSO容量 | sizeof |
MSVC | 15 | 32 | 7 | 32 | 3 | 32 |
GCC | 15 | 32 | 15 | 48 | 15 | 80 |
所以Ubuntu中,当两个string的size一个大于SSO容量15,一个小于15,那么大于15的string不是存储在string对象本身里面,将
其复制给小于15的string时,就会是size小于15的string对象本身的字符串清空并单独开辟一个空间,所以导致size小于15的string
的迭代器、引用、指针就会失效,而size大于15的string的迭代器没有失效。而且这里类似于Vector的交换,交换的是数据结构,
而不是元素的值。
如以下程序所示
string s1{ "123" };
string s2{ "456788888888888888888" };
auto it1 = s1.begin();
auto it2 = s2.begin();
auto &a=s1[1];
cout << "交换前迭代器it1指向的元素为:" <<&it1<<" "<<*it1 << endl;
cout << "交换前迭代器it2指向的元素为:" <<&it2<<" "<<*it2 << endl;
cout << "交换前迭代器a指向的元素为:" << a << endl;
cout<<s1.size();
swap(s1, s2);
// s1.swap(s2);
cout << endl << endl;
cout << "交换后迭代器it1指向的元素为:" <<&it1<< " "<<*it1 << endl;
cout << "交换后迭代器it2指向的元素为:" <<&it2<< " "<<*it2 << endl;
cout << "交换后迭代器a指向的元素为:" << a << endl;
return 0;
---------------------
参考原文:https://blog.csdn.net/qq_43152052/article/details/95793892
原作者:_Fast