vector 内存大小管理

vector 内存大小管理

环境: windows下 vs2019

1. size() 和 capacity()

1.1 定义一个空的 vector

vector<int> v;
cout << v.size() << endl;  //  0
cout << v.capacity() << endl;  // 0

空的 vector 的 size() 和 capacity() 都是 0

capacity() 的值应该受具体编译器的影响,只是在 vs2019 下等于 0,其它环境可能不为 0,但是size() 一定是 0

1.2 使用 reserve() 分配空间

reserve() 影响 capacity()

vector<int> v;

v.reserve(10);
cout << v.size() << endl;            //0
cout << v.capacity() << endl;        //10

注意 reserve() 的空间是不能使用下标访问的

v[0] = 10;   // 会报错!!!

1.3 往 vector 中连续放入 5 个元素

使用push_back()

vector<int> v;

v.reserve(10);

v.push_back(0);
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);

cout << v.size() << endl;           //5
cout << v.capacity() << endl;       //10
size()capacity()
元素的个数实际分配的内存,不分配新空间的情况下,允许放入的最多元素数量

"vector 内存示意图"

由此可以看到, reserve() 负责的是 capacity() 的大小

1.4 reserve() 只能增大 capacity() , 无法缩小 capacity()

尝试使用 reserve() 缩小 capacity()

vector<int> v;

v.reserve(10);

v.push_back(0);
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);

v.reserve(8);
cout << v.size() << endl;           //5  
cout << v.capacity() << endl;       //10

v.reserve(4);
cout << v.size() << endl;            //5 
cout << v.capacity() << endl;        //10

可以看到 vector 的内存大小不受影响

使用 reserve() 扩大 capacity()

vector 增大内存空间时,不是直接在原来内存的末尾增大空间,而是寻找新的空间,大小等于增大后的空间大小,然后将所有元素 移动 到新的空间,并销毁原来的空间

为验证元素被移动,这里打印 reserve() 前后 v[0] 的地址

vector<int> v;

v.reserve(10);

v.push_back(0);
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);

cout << &v << endl;         //00AFFDA8
cout << &v[0] << endl;      //00E9E9E8
v.reserve(15);           
cout << v.size() << endl;     //5
cout << v.capacity() << endl; //15

cout << &v << endl;       //00AFFDA8   
cout << &v[0] << endl;    //00E96C30

可以看到 capacity() 已经增大到 15,而且 v[0] 的地址确实发生改变了

注意 vector 这个对象本身的地址没有改变,因为 vector 本身的实现是个模板对象,里面存放了指针,依靠这些指针指向了 元素 的存储空间,我们改变的是 元素 的存储空间,只会影响到指针的值,对于封装了这些指针的 vector 本身而言,其在内存中的位置并没有发生改变

"reserve()增大内存"

reserve() 的动作:开辟了新的更大的空间,移动了元素,销毁了原来的空间,并修改了指针

1.5 向 vector 中插入元素

现在一直向 vector 中 push_back 元素,直到 size() == capacity() , 然后再 push_back 一个元素

vector<int> v;

v.reserve(10);

v.push_back(0);
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);

v.reserve(15);

cout << &v[0] << endl;  //0063F688
int k = 5;
// 放入元素,占满 v 的内存空间
while (v.size() != v.capacity())
    v.push_back(k++);

cout << v.size() << endl;      //15
cout << v.capacity() << endl;  //15

cout << &v[0] << endl;        //0063F688 未超出 capacity 之前,元素没有移动

// 达到 capacity() 上限后再次 push_back 元素
v.push_back(k++);
cout << v.size() << endl;          //16
cout << v.capacity() << endl;      //22
cout << &v[0] << endl;             //006355C0    超出 capacity 后,元素发生移动,且capacity() 增大了很多,即预留了一些空间,给后续插入使用

所以 当插入的元素达到 capacity() 上限后,继续插入元素,就需要寻找新的更大的空间,发生元素移动,同时销毁原来的空间,并调整capacity() , 预留一些空间,capacity() 增大多少依赖于环境,这里从 15 变成了 22

如果继续增大超过 capacity()

vector<int> v;

v.reserve(10);

v.push_back(0);
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);

v.reserve(15);

int k = 5;
while (v.size() != v.capacity())
    v.push_back(k++);

v.push_back(k++);
cout << v.size() << endl;          //16
cout << v.capacity() << endl;      //22

while (v.size() != v.capacity())
    v.push_back(k++);
v.push_back(k++);
cout << v.size() << endl;           //23
cout << v.capacity() << endl;       //33

不清楚 capacity() 增大的量怎么计算的,反正就是增加了不少

C++ primer 是 将 capacity() 的值翻倍

1.6 shrink_to_fit()

shrink_to_fit() 将 capacity() 减小到 size() , 也就是回收了多分配而未使用的空间

vector<int> v;

v.reserve(10);

v.push_back(0);
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);

v.reserve(15);

int k = 5;
while (v.size() != v.capacity())
    v.push_back(k++);

v.push_back(k++);

while (v.size() != v.capacity())
    v.push_back(k++);
v.push_back(k++);
cout << v.size() << endl;      //23
cout << v.capacity() << endl;  //33

v.shrink_to_fit();  // 回收多余未使用的空间
cout << v.size() << endl;       //23
cout << v.capacity() << endl;   //33

这里 v.shrink_to_fit() 回收了空间,现在 size() == capacity()

但是 C++ primer 中也说明 shrink_to_fit() 只是个请求,请求回收空间,这个回收请求也可能不被实现,所以内存也可能没有回收,要看具体环境

1.7 resize()

resize() 影响 size()

resize()reserve()
说明调整元素的个数 size()调整空间大小 capacity(),不会对已有的元素产生任何影响
区别1. 影响 size() ,既能增大 size(), 也能减小 size()
2. 如果增大后 size() 超过了 capacity(),就需要开辟新空间,移动元素,capacity() 的值也会增大。
3. 如果调整后 size() 不超过 capacity(), capacity() 就不受影响
4. 如果调整后的 size() 超过原来的元素数量,原来元素的值不发生变换,新增的值使用默认初始化或者由 size() 提供初始值
5. 如果调整后的 size() 小于原来元素的数量,只会将多余的元素去掉,且剩余的元素不发生任何变化,值保持不变,即使 size() 提供了初始值,也不会改变,size() 提供的初始值只会影响新增的值
影响 capacity(),且只能增大 capacity(), 无法减小 capacity()

resize (n, t)

n:调整后 size() 的大小,即调整 vector 元素的个数

t:如果 size() 增大了,用 t 初始化新增加的元素,如果 size() 减小了,t 没有作用;t 只对新增的值产生作用

​ t 是可选参数,没有 t 的话,新增的元素就使用默认初始化

// 重载 << , 输出 vector<int>
ostream& operator<< (ostream& os,vector<int>& v)
{
	for (auto& i : v)
		os << i << '\t';

	return os;
}

vector<int> v;
v.reserve(10);

v.push_back(0);
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);


cout << v << endl;                      //0       1       2       3       4
cout << &v[0] << endl;                  // 013F9980
cout << v.size() << endl;               //5
cout << v.capacity() << endl << endl;   //10

//将 v 的元素扩大到 8 个, 新增的元素使用默认初始化,即 0
v.resize(8);              
cout << v << endl;            //0       1       2       3       4       0       0       0
cout << &v[0] << endl;        //013F9980 没有开辟新空间,没有移动
cout << v.size() << endl;     //8
cout << v.capacity() << endl<<endl;  //10

//将 v 的元素缩小到 3 个,数量减小, 参数 11 不起作用
v.resize(3,11);
cout << v << endl;            //0       1       2
cout << &v[0] << endl;        //013F9980 没有开辟新空间,没有移动
cout << v.size() << endl;     //3
cout << v.capacity() << endl<<endl;  //10

//将 v 的元素扩大到 6 个, 新增的元素使用置为222
v.resize(6,222);
cout << v << endl;         //0       1       2       222     222     222
cout << &v[0] << endl;     //013F9980 没有开辟新空间,没有移动
cout << v.size() << endl;  //6
cout << v.capacity() << endl<<endl; //10 

//将 v 的元素扩大到 22 个, 新增的元素使用置为333
v.resize(20, 333);
cout << v << endl;   //0       1       2       222     222     222     333     333     333     333     333     333     333     333     333    333      333     333     333     333
cout << &v[0] << endl;       //013F4678 开辟新空间,元素移动
cout << v.size() << endl;    //20 
cout << v.capacity() << endl << endl;   //20

可以看到,当调整 size() 超过 capacity() 时,新空间的 capacity() == size(),并没有预留多的空间 , 而没有像 1.5 中那样把 capacity() 设置较大。

1.8 assign()

// 将 [first,last) 的元素依次放入 vector 中,直接覆盖了 vector 原来的所有元素
void assign(const_iterator first,const_iterator last);
 
// 将 n 个 x 依次放入 vector 中,直接覆盖了 vector 原来的所有元素
void assign(size_type n,const T& x = T());

原来的元素都不要了,全部换成新的元素

示例1 { 1, 2, 3, 4, 5 }→ { 100, 200, 300 }

ostream& operator<<(ostream& os, vector<int>& v)
{
	for (auto& i : v)
		cout << i << '\t';
	return os;
}

int main()
{

	vector<int> v = { 1,2,3,4,5 };
	
	cout << "原始 vector<int> " << endl;
	cout << v << endl;
	cout <<"size() = "<< v.size() << endl;
	cout<<"capacity() = " << v.capacity() << endl;
	
    /*
        原始 vector<int>
        1       2       3       4       5
        size() = 5
        capacity() = 5
    */
	cout << endl << endl << endl;
	cout << "使用 assign 重新分配元素" << endl;
	v.assign({ 100,200,300 });
	
	cout << v << endl;

	cout <<"size() = " << v.size() << endl;
	cout <<"capacity() = " << v.capacity() << endl;

    /*
        使用 assign 重新分配元素
        100     200     300
        size() = 3
        capacity() = 5  
    */
	return 0;
}

示例2 { 9 }→ { 100, 200, 300 }

int main()
{

	vector<int> v = { 9 };
	
	cout << "原始 vector<int> " << endl;
	cout << v << endl;
	cout <<"size() = "<< v.size() << endl;
	cout<<"capacity() = " << v.capacity() << endl;
	
	/*
		原始 vector < int>
		9
		size() = 1
		capacity() = 1
	*/

	cout << endl << endl << endl;
	cout << "使用 assign 重新分配元素" << endl;
	v.assign({ 100,200,300 });
	
	cout << v << endl;

	cout <<"size() = " << v.size() << endl;
	cout <<"capacity() = " << v.capacity() << endl;


	/*
		使用 assign 重新分配元素
		100     200     300
		size() = 3
		capacity() = 3
	*/
	return 0;
}

示例3 { }→ { 100, 200, 300 }

int main()
{

	vector<int> v ;
	
	cout << "原始 vector<int> " << endl;
	cout << v << endl;
	cout <<"size() = "<< v.size() << endl;
	cout<<"capacity() = " << v.capacity() << endl;
	
	/*
		原始 vector<int>

		size() = 0
		capacity() = 0
	*/

	cout << endl << endl << endl;
	cout << "使用 assign 重新分配元素" << endl;
	v.assign({ 100,200,300 });
	
	cout << v << endl;

	cout <<"size() = " << v.size() << endl;
	cout <<"capacity() = " << v.capacity() << endl;


	/*
		使用 assign 重新分配元素
		100     200     300
		size() = 3
		capacity() = 3

	*/
	return 0;
}
resize()assign()
说明调整元素的个数 size()直接用新的元素覆盖原来的元素
区别1. 影响 size() ,既能增大 size(), 也能减小 size()
2. 如果增大后 size() 超过了 capacity(),就需要开辟新空间,移动元素,capacity() 的值也会增大, 但是 capacity() 没有预留更大的空间,而是 capacity() == size()。
3. 如果调整后 size() 不超过 capacity(), capacity() 就不受影响
4. 如果调整后的 size() 超过原来的元素数量,原来元素的值不发生变换,新增的值使用默认初始化或者由 size() 提供初始值
5. 如果调整后的 size() 小于原来元素的数量,只会将多余的元素去掉,且剩余的元素不发生任何变化,值保持不变,即使 size() 提供了初始值,也不会改变,size() 提供的初始值只会影响新增的值
1. 如果 assign() 需要的空间小于等于 capacity(), 不需要开辟新空间,只需要在原来的空间上重新分配元素即可,旧的元素都会被覆盖
2. 如果 assign() 需要的空间大于 capacity() ,那么可能需要开辟新的空间,也可能不用 (不清楚原因,好像直接在原来的空间后面续了一段空间,可能和具体内存情况有关,要是后面还有空间就直接用了,不需要全部重新分配),如果发生了重新分配内存,capacity() 也会被设置为更大的值,预留一定的空间

简化版本区别

resize()assign()
修改后元素数量 > 原始 capacity() >= 原始 size()发生新空间开辟,元素移动,旧空间销毁
新空间:size() ==capacity()
没有预留空间
旧元素值不变(已发生移动),新增元素默认初始化或者 使用提供的初始值初始化
可能发生新空间开辟,元素移动,旧空间销毁,也可能直接在原空间续一段空间
新空间:size() < capacity()
预留了一定空间
旧元素清空,全部替换成新元素
原始 capacity() > 修改后元素数量 > 原始 size()旧元素不变,新增元素默认初始化或者 使用提供的初始值初始化旧元素清空,全部替换成新元素
原始 capacity() > 原始 size() > 修改后元素数量只把多出来的部分删除,剩余旧元素不变旧元素清空,全部替换成新元素

2. vector clear() 清空元素

2.1 元素为普通内置类型, 如 int

// 重载 << , 输出 vector<int>
ostream& operator<< (ostream& os, vector<int>& v)
{
	for (auto& i : v)
		os << i << '\t';

	return os;
}

vector<int> v;
v.reserve(10);

v.push_back(0);
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);

cout << v << endl;                      // 0       1       2       3       4
cout << &v[0] << endl;                  // 0080AE80
cout << v.size() << endl;               // 5
cout << v.capacity() << endl << endl;   // 10

v.clear();

cout << v << endl;                      // 无输出
cout << v.size() << endl;               // 0
cout << v.capacity() << endl << endl;   // 10

调用 clear() 之后, size() 为 0, 所有的元素都清空了, 但是 capacity() 保持不变, 也就是内存空间没有销毁

2.2 元素为类类型

由于 是 类类型,可以定义 类的 析构函数 ,当 vector 的 capacity() 不够时,需要开辟新空间,就会发生元素移动后 (拷贝构造函数),而在销毁旧空间的元素时,会调用 析构函数

2.2.1 push_back()

class base {
	friend ostream& operator<<(ostream& os, base& b);
private:
	int a = 1;
	string name;
public:
	base(string _name, int _a = 1) :name(_name), a(_a) { cout << name << "---" << a << "   construct" << endl; }
	base(const base& r):name(r.name),a(r.a) { cout<<name<<"---"<<a << "   copy()"<<endl; }  //拷贝构造
	//base(base&& r) :name(std::move(r.name)), a(std::move(r.a))  { cout << name << "---" << a << "   move()" << endl; }  // 移动构造
	~base() { cout << name << "---" << a << "   ~base()  "<< endl; }  // 析构函数


};

ostream& operator<<(ostream& os,base& b)
{
	os << b.name<<"---"<<b.a;
	return os;
}

// 重载 << , 输出 vector<int>
ostream& operator<< (ostream& os, vector<base>& v)
{
	for (auto& i : v)
		os << i << '\t';

	return os;
}

int main()
{
	vector<base> v;

	cout<<"原始 vector 的容量 : " << v.capacity() << endl;
	
	v.emplace_back("base", 1);
	cout << "插入第 1 个元素后, vector 的容量为 :" << v.capacity() << "   " << v << endl;

    /*
        原始 vector 的容量 : 0
        base---1   construct
        插入第 1 个元素后, vector 的容量为 :1   base---1
    */
    
	cout << endl;
	cout << "尝试插入第 2 个元素" << endl;
	v.emplace_back("base", 2);
	cout << "插入第 2 个元素后, vector 的容量为 :" << v.capacity()<<"   "<<v << endl;

    /*
        尝试插入第 2 个元素
        base---2   construct
        base---1   copy()
        base---1   ~base()
        插入第 2 个元素后, vector 的容量为 :2   base---1        base---2
    */
    // 空间不够,发生了旧元素的拷贝,销毁
    
	cout << endl;
	cout << "尝试插入第 3 个元素" << endl;
	v.emplace_back("base", 3);
	cout << "插入第 3 个元素后, vector 的容量为 :" << v.capacity() << "   " << v << endl;

    /*
        尝试插入第 3 个元素
        base---3   construct
        base---1   copy()
        base---2   copy()
        base---1   ~base()
        base---2   ~base()
        插入第 3 个元素后, vector 的容量为 :3   base---1        base---2        base---3
    */
    // 空间不够,发生了旧元素的拷贝,销毁
    
	cout << endl;
	cout << "尝试插入第 4 个元素" << endl;
	v.emplace_back("base", 4);
	cout << "插入第 4 个元素后, vector 的容量为 :" << v.capacity() << "   " << v << endl;
    /*
        尝试插入第 4 个元素
        base---4   construct
        base---1   copy()
        base---2   copy()
        base---3   copy()
        base---1   ~base()
        base---2   ~base()
        base---3   ~base()
        插入第 4 个元素后, vector 的容量为 :4   base---1        base---2        base---3        base---4
    */
    // 空间不够,发生了旧元素的拷贝,销毁
    
	cout << endl;
	cout << "尝试插入第 5 个元素" << endl;
	v.emplace_back("base", 5);
	cout << "插入第 5 个元素后, vector 的容量为 :" << v.capacity() << "   " << v << endl;

    /*
        尝试插入第 5 个元素
        base---5   construct
        base---1   copy()
        base---2   copy()
        base---3   copy()
        base---4   copy()
        base---1   ~base()
        base---2   ~base()
        base---3   ~base()
        base---4   ~base()
        插入第 5 个元素后, vector 的容量为 :6   base---1        base---2        base---3        base---4        base---5
    */
    // 空间不够,发生了旧元素的拷贝,销毁
    
	cout << endl;
	cout << "尝试插入第 6 个元素" << endl;
	v.emplace_back("base", 6);
	cout << "插入第 6 个元素后, vector 的容量为 :" << v.capacity() << "   " << v << endl;

    /*
        尝试插入第 6 个元素
        base---6   construct
        插入第 6 个元素后, vector 的容量为 :6   base---1        base---2        base---3        base---4        base---5       base---6
    */
    // 空间够,没有发生旧元素的拷贝,销毁

	cout << endl << "程序结束, 自动销毁 vector , 调用每个 元素 的 析构函数" << endl;
    /*
        程序结束, 自动销毁 vector , 调用每个 元素 的 析构函数
        base---1   ~base()
        base---2   ~base()
        base---3   ~base()
        base---4   ~base()
        base---5   ~base()
        base---6   ~base()
    */
	return 0;
}

输出如下:

原始 vector 的容量 : 0
base---1   construct
插入第 1 个元素后, vector 的容量为 :1   base---1

尝试插入第 2 个元素
base---2   construct
base---1   copy()
base---1   ~base()
插入第 2 个元素后, vector 的容量为 :2   base---1        base---2

尝试插入第 3 个元素
base---3   construct
base---1   copy()
base---2   copy()
base---1   ~base()
base---2   ~base()
插入第 3 个元素后, vector 的容量为 :3   base---1        base---2        base---3

尝试插入第 4 个元素
base---4   construct
base---1   copy()
base---2   copy()
base---3   copy()
base---1   ~base()
base---2   ~base()
base---3   ~base()
插入第 4 个元素后, vector 的容量为 :4   base---1        base---2        base---3        base---4

尝试插入第 5 个元素
base---5   construct
base---1   copy()
base---2   copy()
base---3   copy()
base---4   copy()
base---1   ~base()
base---2   ~base()
base---3   ~base()
base---4   ~base()
插入第 5 个元素后, vector 的容量为 :6   base---1        base---2        base---3        base---4        base---5

尝试插入第 6 个元素
base---6   construct
插入第 6 个元素后, vector 的容量为 :6   base---1        base---2        base---3        base---4        base---5       base---6

程序结束, 自动销毁 vector , 调用每个 元素 的 析构函数
base---1   ~base()
base---2   ~base()
base---3   ~base()
base---4   ~base()
base---5   ~base()
base---6   ~base()

注意使用 emplace_back () ,直接在 vector 的空间中构造对象, 如果 是 push_back,会先构造 临时对象, 然后把临时对象复制到 vector 的空间,随后立刻销毁 临时对象,这样临时对象会 产生一次 构造函数 和 析构函数。

2.2.2 emplace_back()

现在使用 使用 push_back() 代替 emplace_back()

vector<base> v;

cout<<"原始 vector 的容量 : " << v.capacity() << endl;

//v.emplace_back("base", 1);
v.push_back({ "base", 1 });
cout << "插入第 1 个元素后, vector 的容量为 :" << v.capacity() << "   " << v << endl;

/*
    原始 vector 的容量 : 0
    base---1   construct
    base---1   copy()
    base---1   ~base()
    插入第 1 个元素后, vector 的容量为 :1   base---1
*/

// 发生临时对象(base1)的构造,拷贝,销毁

cout << endl;
cout << "尝试插入第 2 个元素" << endl;
//v.emplace_back("base", 2);
v.push_back({ "base", 2 });
cout << "插入第 2 个元素后, vector 的容量为 :" << v.capacity()<<"   "<<v << endl;

/*
    尝试插入第 2 个元素
    base---2   construct
    base---2   copy()
    base---1   copy()
    base---1   ~base()
    base---2   ~base()
    插入第 2 个元素后, vector 的容量为 :2   base---1        base---2
*/

// 发生临时对象(base2)的构造,拷贝,销毁, 原始空间不够,发生了旧元素的拷贝,销毁

cout << endl;
cout << "尝试插入第 3 个元素" << endl;
//v.emplace_back("base", 3);
v.push_back({ "base", 3 });
cout << "插入第 3 个元素后, vector 的容量为 :" << v.capacity() << "   " << v << endl;
/*
    尝试插入第 3 个元素
    base---3   construct
    base---3   copy()
    base---1   copy()
    base---2   copy()
    base---1   ~base()
    base---2   ~base()
    base---3   ~base()
    插入第 3 个元素后, vector 的容量为 :3   base---1        base---2        base---3
*/
// 发生临时对象(base3)的构造,拷贝,销毁, 原始空间不够,发生了旧元素的拷贝,销毁

cout << endl;
cout << "尝试插入第 4 个元素" << endl;
//v.emplace_back("base", 4);
v.push_back({ "base", 4 });
cout << "插入第 4 个元素后, vector 的容量为 :" << v.capacity() << "   " << v << endl;
/*
    尝试插入第 4 个元素
    base---4   construct
    base---4   copy()
    base---1   copy()
    base---2   copy()
    base---3   copy()
    base---1   ~base()
    base---2   ~base()
    base---3   ~base()
    base---4   ~base()
    插入第 4 个元素后, vector 的容量为 :4   base---1        base---2        base---3        base---4
*/
// 发生临时对象(base4)的构造,拷贝,销毁, 原始空间不够,发生了旧元素的拷贝,销毁

cout << endl;
cout << "尝试插入第 5 个元素" << endl;
//v.emplace_back("base", 5);
v.push_back({ "base", 5 });
cout << "插入第 5 个元素后, vector 的容量为 :" << v.capacity() << "   " << v << endl;
/*
    尝试插入第 5 个元素
    base---5   construct
    base---5   copy()
    base---1   copy()
    base---2   copy()
    base---3   copy()
    base---4   copy()
    base---1   ~base()
    base---2   ~base()
    base---3   ~base()
    base---4   ~base()
    base---5   ~base()
    插入第 5 个元素后, vector 的容量为 :6   base---1        base---2        base---3        base---4        base---5
*/
// 发生临时对象(base6)的构造,拷贝,销毁, 原始空间不够,发生了旧元素的拷贝,销毁

cout << endl;
cout << "尝试插入第 6 个元素" << endl;
//v.emplace_back("base", 6);
v.push_back({ "base", 6 });
cout << "插入第 6 个元素后, vector 的容量为 :" << v.capacity() << "   " << v << endl;
/*
    尝试插入第 6 个元素
    base---6   construct
    base---6   copy()
    base---6   ~base()
    插入第 6 个元素后, vector 的容量为 :6   base---1        base---2        base---3        base---4        base---5       base---6
*/


cout << endl << "程序结束, 自动销毁 vector , 调用每个 元素 的 析构函数" << endl;
/*
    程序结束, 自动销毁 vector , 调用每个 元素 的 析构函数
    base---1   ~base()
    base---2   ~base()
    base---3   ~base()
    base---4   ~base()
    base---5   ~base()
    base---6   ~base()
*/

输出如下:

原始 vector 的容量 : 0
base---1   construct
base---1   copy()
base---1   ~base()
插入第 1 个元素后, vector 的容量为 :1   base---1

尝试插入第 2 个元素
base---2   construct
base---2   copy()
base---1   copy()
base---1   ~base()
base---2   ~base()
插入第 2 个元素后, vector 的容量为 :2   base---1        base---2

尝试插入第 3 个元素
base---3   construct
base---3   copy()
base---1   copy()
base---2   copy()
base---1   ~base()
base---2   ~base()
base---3   ~base()
插入第 3 个元素后, vector 的容量为 :3   base---1        base---2        base---3

尝试插入第 4 个元素
base---4   construct
base---4   copy()
base---1   copy()
base---2   copy()
base---3   copy()
base---1   ~base()
base---2   ~base()
base---3   ~base()
base---4   ~base()
插入第 4 个元素后, vector 的容量为 :4   base---1        base---2        base---3        base---4

尝试插入第 5 个元素
base---5   construct
base---5   copy()
base---1   copy()
base---2   copy()
base---3   copy()
base---4   copy()
base---1   ~base()
base---2   ~base()
base---3   ~base()
base---4   ~base()
base---5   ~base()
插入第 5 个元素后, vector 的容量为 :6   base---1        base---2        base---3        base---4        base---5

尝试插入第 6 个元素
base---6   construct
base---6   copy()
base---6   ~base()
插入第 6 个元素后, vector 的容量为 :6   base---1        base---2        base---3        base---4        base---5       base---6

程序结束, 自动销毁 vector , 调用每个 元素 的 析构函数
base---1   ~base()
base---2   ~base()
base---3   ~base()
base---4   ~base()
base---5   ~base()
base---6   ~base()
2.2.3 调用 clear()

使用 vector 保存 base 对象,调用 clear() 会自动销毁对象,自动调用 析构函数

class base {
	friend ostream& operator<<(ostream& os, base& b);
private:
	int a = 1;
	string name;
public:
	base(string _name, int _a = 1) :name(_name), a(_a) { cout << name << "---" << a << "   construct" << endl; }
	base(const base& r):name(r.name),a(r.a) { cout<<name<<"---"<<a << "   copy()"<<endl; }  //拷贝构造
	//base(base&& r) :name(std::move(r.name)), a(std::move(r.a))  { cout << name << "---" << a << "   move()" << endl; }  // 移动构造
	~base() { cout << name << "---" << a << "   ~base()  "<< endl; }  // 析构函数

};

int main()
{

	vector<base> v = { base("base",1), base("base",2), base("base",3) };
    /*
        base---1   construct
        base---2   construct
        base---3   construct
        base---1   copy()
        base---2   copy()
        base---3   copy()
        base---3   ~base()
        base---2   ~base()
        base---1   ~base()
    */
    // 此处仍然发生了临时对象的创建,拷贝销毁

	cout << "调用 clear()" << endl;
	v.clear();

	cout << "clear() 结束" << endl;
	/*
        调用 clear()
        base---1   ~base()
        base---2   ~base()
        base---3   ~base()
        clear() 结束
	*/
    // 调用 clear() 会自动调用对象的 析构函数
    
	return 0;
}
2.2.4 遗留个问题

在 vector 空间不足的时候,怎么使用移动构造,而非拷贝构造

2.3 元素类型为指针

当 vector 中的元素为 指针 时,在 clear() 之前如果不对指针进行 delete, 就会发生 内存泄露

2.3.1 vs2019 内存泄露检查

通过 new 动态申请空间,但是没有 delete

#include<iostream>
using namespace std;

#define _CRTDBG_MAP_ALLOC

int main()
{
	_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);

	int* p = new int(10);
    //delete p;
	return 0;
}

F5 调试后,在输出窗口检测到 内存泄露

Detected memory leaks!
Dumping objects ->
{152} normal block at 0x016965E0, 4 bytes long.
 Data: <    > 0A 00 00 00

重新设置 delete 后就不会有内存泄露

2.3.2 vector<int *>

使用 vector 保存 int 指针,调用 clear() 之前 没有 使用 delete p

int main()
{
	_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);

	vector<int*> v = { new int(1),new int(2),new int(3) };

	v.clear();

	return 0;
}

F5 调试后,在输出窗口检测到 内存泄露

{156} normal block at 0x00FC6680, 4 bytes long.
 Data: <    > 03 00 00 00 
{155} normal block at 0x00FC5E20, 4 bytes long.
 Data: <    > 02 00 00 00 
{154} normal block at 0x00FCEF80, 4 bytes long.
 Data: <    > 01 00 00 00

修改代码,clear() 前进行 delete 指针

int main()
{
	_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);

	vector<int*> v = { new int(1),new int(2),new int(3) };

	for (auto p : v)
		delete p;
    
	v.clear();

	return 0;
}

此时没有发生 内存泄露

2.3.3 vector<base*>

使用 vector 保存 base 指针,调用 clear() 之前 没有 使用 delete

#define _CRTDBG_MAP_ALLOC


class base {
	friend ostream& operator<<(ostream& os, base& b);
private:
	int a = 1;
	string name;
public:
	base(string _name, int _a = 1) :name(_name), a(_a) { cout << name << "---" << a << "   construct" << endl; }
	base(const base& r):name(r.name),a(r.a) { cout<<name<<"---"<<a << "   copy()"<<endl; }  //拷贝构造
	//base(base&& r) :name(std::move(r.name)), a(std::move(r.a))  { cout << name << "---" << a << "   move()" << endl; }  // 移动构造
	~base() { cout << name << "---" << a << "   ~base()  "<< endl; }  // 析构函数


};


int main()
{
	_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);

	vector<base*> v = { new base("base",1), new base("base",2), new base("base",3) };
	/*
        base---1   construct
        base---2   construct
        base---3   construct
	*/
    
	cout << "调用 clear()" << endl;
	v.clear();

	cout << "clear() 结束" << endl;
	/*
        调用 clear()
        clear() 结束
	*/
    //没有调用 base 对象的 析构函数
	return 0;

}

F5 调试后,在输出窗口检测到 内存泄露

Detected memory leaks!
Dumping objects ->
{200} normal block at 0x00BAF258, 8 bytes long.
 Data: <        > B4 0E BB 00 00 00 00 00 
{198} normal block at 0x00BB0EB0, 32 bytes long.
 Data: <    X   base    > 03 00 00 00 58 F2 BA 00 62 61 73 65 00 8B AE 00 
{195} normal block at 0x00BAF530, 8 bytes long.
 Data: <        > 94 10 BB 00 00 00 00 00 
{193} normal block at 0x00BB1090, 32 bytes long.
 Data: <    0   base    > 02 00 00 00 30 F5 BA 00 62 61 73 65 00 8B AE 00 
{156} normal block at 0x00BAEFF0, 8 bytes long.
 Data: < f      > 84 66 BA 00 00 00 00 00 
{154} normal block at 0x00BA6680, 32 bytes long.
 Data: <        base    > 01 00 00 00 F0 EF BA 00 62 61 73 65 00 8B AE 00 
Object dump complete.

修改代码,clear() 前进行 delete 指针

int main()
{
	_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
	vector<base*> v = { new base("base",1), new base("base",2), new base("base",3) };
    /*
        base---1   construct
        base---2   construct
        base---3   construct
    */
	cout<<"对 对象指针 进行 delete"<<endl;
	for (auto p : v)
		delete p;
    /*
        对 对象指针 进行 delete
        base---1   ~base()
        base---2   ~base()
        base---3   ~base()
    */

	cout << "调用 clear()" << endl;
	v.clear();

	cout << "clear() 结束" << endl;
	/*
        调用 clear()
        clear() 结束
	*/
	return 0;

}

可以看到,对 base 对象指针进行 delete 时,调用了对象的析构函数,销毁了对象的空间

最后没有产生内存泄露

2.4 vector clear() 小结

元素为对象时,调用 clear() 会自动调用析构函数

元素为指针时,指针指向的对象无法析构,因此需要释放指针所指对象的话,需要在clear之前调用delete,否则发生内存泄露

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值