c++ vector拷贝构造_vector------stl学习笔记一

vector其中一个特点:内存空间只会增长,不会减小

援引C++ Primer:为了支持快速的随机访问,vector容器的元素以连续方式存放,每一个元素都紧挨着前一个元素存储。

设想一下,当vector添加一个元素时,为了满足连续存放这个特性,都需要重新分配空间、拷贝元素、撤销旧空间,这样性能难以接受。因此STL实现者在对vector进行内存分配时,其实际分配的容量要比当前所需的空间多一些。就是说,vector容器预留了一些额外的存储区,用于存放新添加的元素,这样就不必为每个新元素重新分配整个容器的内存空间。

原文:https://www.cnblogs.com/biyeymyhjob/archive/2012/09/12/2674004.html


push_back复杂度O(1),插入最坏情况的复杂度为O(n)。

不需要在其他位置插入元素,需要随机访问,用vector是比较不错的。

要是有时候需要中间和其他位置的插入和删除,那么list可能是个不错的选择。


push_back发生了什么

#include <iostream>
#include <vector>

class Point
{
	public:
		Point(){std::cout<<"construction is called"<<std::endl;}
		Point(const Point& p)
		{
			std::cout<<"copy construction is called"<<std::endl;
		}

		~Point()
		{
			std::cout<<"destruction is called"<<std::endl;
		}
};

int main()
{
	std::vector<Point> pointVec;
	Point a;
	Point b;
	pointVec.push_back(a);
	pointVec.push_back(b);

	std::cout<<pointVec.size()<<std::endl;
	return 0;
}

2074683fd28b1cc37ab53089bd370896.png

其中执行

pointVec.push_back(a);

此时vector会申请一个内存空间,并调用拷贝构造函数将a放到vector中

再调用

pointVec.push_back(b);

此时内存不够 需要扩大内存,重新分配内存 这时再调用拷贝构造函数将a拷贝到新的内存,再将b拷入新的内存(所以拷贝构造函数调用了三次),紧接着将原来的vector的内存释放所以调用一次析构函数释放a,然后函数结束,pointVec(含有a和b)和最初的a,b两个对象被释放,所以调用四次析构函数


拷贝构造函数的调用时机

  • 当函数的参数为类的对象时(这是引用 :Book &book 这是对象:Book book
  • 函数的返回值是类的对象(不是引用)
  • 对象需要通过另外一个对象进行初始化

vector是怎么扩大容量的

现在改一下上面的main函数

int main()
{
	std::vector<Point> pointVec;
	Point a;
	Point b;
	pointVec.push_back(a);
	std::cout<<"size="<<pointVec.size()<<std::endl;
	std::cout<<"capacity="<<pointVec.capacity()<<std::endl;
	pointVec.push_back(b);

	std::cout<<"size="<<pointVec.size()<<std::endl;
        std::cout<<"capacity="<<pointVec.capacity()<<std::endl;

	pointVec.push_back(b);
	pointVec.push_back(a);
	std::cout<<"size="<<pointVec.size()<<std::endl;
        std::cout<<"capacity="<<pointVec.capacity()<<std::endl;
	pointVec.push_back(b);	
       	std::cout<<"size="<<pointVec.size()<<std::endl;
        std::cout<<"capacity="<<pointVec.capacity()<<std::endl;

	return 0;
}

下面使是输出内容 目的是为了看一下vector怎么分配内存的(注意capacity()容器的容量不等于size())

每次超出 vector都会扩大一倍容量

$ ./test 
construction is called
construction is called
copy construction is called
size=1
capacity=1
copy construction is called
copy construction is called
destruction is called
size=2
capacity=2
copy construction is called
copy construction is called
copy construction is called
destruction is called
destruction is called
copy construction is called
size=4
capacity=4
copy construction is called
copy construction is called
copy construction is called
copy construction is called
copy construction is called
destruction is called
destruction is called
destruction is called
destruction is called
size=5
capacity=8
destruction is called
destruction is called
destruction is called
destruction is called
destruction is called
destruction is called
destruction is called

在引入右值引用,转移构造函数,转移复制运算符之前,通常使用push_back()向容器中加入一个右值元素(临时对象)的时候,首先会调用构造函数构造这个临时对象,然后需要调用拷贝构造函数将这个临时对象放入容器中。原来的临时变量释放。这样造成的问题是临时变量申请的资源就浪费。

原文:https://blog.csdn.net/MOU_IT/article/details/88757348


vector emplace_back和push_back()区别

临时对象创建的时候用emplace_back和push_back()有区别,如果是下面这样则没有区别

#include <iostream>
#include <vector>

class Point
{
	public:
		Point(){std::cout<<"construction is called"<<std::endl;}
		Point(const Point& p)
		{
			std::cout<<"copy construction is called"<<std::endl;
		}
		
		Point(Point&& ohter)
                {  
			std::cout<<"moveConstruction is called"<<std::endl;
		}
};

int main()
{
	std::vector<Point> pointVec1;
	std::vector<Point> pointVec2;
	Point a;
	Point b;
	std::cout<<"push_back(a):"<<std::endl;
	pointVec1.push_back(a);

	std::cout<<"emplace_back(a):"<<std::endl;
	pointVec2.emplace_back(a);
	
	
	std::cout<<"return 0;"<<std::endl;
	return 0;
}

输出 可以看出这么做并没有区别

$ ./test 
construction is called
construction is called
push_back(a):
copy construction is called
emplace_back(a):
copy construction is called
return 0;

而这样 则会有区别:

#include <iostream>
#include <vector>
#include <string>

class Point
{
	public:
		Point(std::string name,int price)  
			:name_(name), price_(price)
		{
			std::cout<<"construction is called"<<std::endl;
		}
		
		
		Point(const Point& other)
			:name_(other.name_), price_(other.price_)
		{
			std::cout<<"copy construction is called"<<std::endl;
		}
		
		Point(Point&& other)
			:name_(other.name_), price_(other.price_)
		{
			std::cout<<"moveConstruction is called"<<std::endl;
		}
	public:
		std::string name_;
		int price_;
};

int main()
{
	std::vector<Point> pointVec1;
	std::vector<Point> pointVec2;
	std::cout<<"push_back:"<<std::endl;
	pointVec1.push_back(Point("banana",13));
	
	std::cout<<"emplace_back:"<<std::endl;
	pointVec2.emplace_back("apple",14);//注意pusk_back不可以这么用
	
	
	std::cout<<"return 0;"<<std::endl;
	return 0;
}

输出:

$ ./test 
push_back:
construction is called
moveConstruction is called
emplace_back:
construction is called
return 0;

总结:

push_back()右值时就会调用构造函数和转移构造函数。进一步优化的空间就是使用emplace_back,在容器尾部添加一个元素,这个元素原地构造不需要触发拷贝构造和转移构造


std::move()

C++ 标准库使用比如vector::push_back 等这类函数时,会对参数的对象进行复制,连数据也会复制.这就会造成对象内存的额外创建, 本来原意是想把参数push_back进去就行了,通过std::move,可以避免不必要的拷贝操作。

在C++11中,标准库在<utility>中提供了一个有用的函数std::move,std::move并不能移动任何东西,它唯一的功能是将一个左值强制转化为右值引用,继而可以通过右值引用使用该值,以用于移动语义。从实现上讲,std::move基本等同于一个类型转换:static_cast<T&&>(lvalue);

std::move函数可以以非常简单的方式将左值引用转换为右值引用。

std::move是将对象的状态或者所有权从一个对象转移到另一个对象,只是转移,没有内存的搬迁或者内存拷贝所以可以提高利用效率,改善性能.。

//摘自https://zh.cppreference.com/w/cpp/utility/move
#include <iostream>
#include <utility>
#include <vector>
#include <string>
int main()
{
    std::string str = "Hello";
    std::vector<std::string> v;
    //调用常规的拷贝构造函数,新建字符数组,拷贝数据
    v.push_back(str);
    std::cout << "After copy, str is "" << str << ""n";
    //调用移动构造函数,掏空str,掏空后,最好不要使用str
    v.push_back(std::move(str));
    std::cout << "After move, str is "" << str << ""n";
    std::cout << "The contents of the vector are "" << v[0]
                                         << "", "" << v[1] << ""n";
}

输出:

After copy, str is "Hello"
After move, str is ""
The contents of the vector are "Hello", "Hello"

std::move 的函数原型定义

template <typename T>
typename remove_reference<T>::type&& move(T&& t)
{
	return static_cast<typename remove_reference<T>::type&&>(t);
}

原文链接:c++ 之 std::move 原理实现与用法总结

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值