先写一个具有拷贝构造,移动构造的一个类(函数体只做现象输出,没有具体实现)
class MyClass
{
public:
MyClass(string str):m_Str(str) { cout << "构造" << endl; }
~MyClass() { cout << "析构" << endl; }
MyClass(const MyClass& ClassTemp) { cout << "拷贝构造" << endl; }
MyClass(MyClass && ClassTemp) { cout << "移动构造" << endl; }
private:
string m_Str;
};
int main()
{
vector<MyClass> sVec;
MyClass str("456");
sVec.push_back(string("123"));
sVec.push_back(str);
system("pause");
return 0;
}
在C++11以前,还没有右值引用这个概念,所以vector的push_back就只有一种实现,void push_back(const value_type& _Val),value_type是模板推导的类型,其实这个函数已经可以满足添加右值的需求了(常引用可以引用右值和左值),那我们现在就运行看看push_back是怎么调用的
这是第一个push_back(string("123"))的内部运行,可以看到,它调用的是void push_back(
value_type&& _val)这个函数,这个函数看过去像是一个右值引用,但是实际上这个是一个通用引用(不知道通用引用的可以先了解一下),我们继续往下运行
运行到这个地方它发现vector里面没有元素,所以调用reserve给这个vector扩容,后面_Orphan_range这个函数进去之后发现现在没有进行什么操作(这个函数里面的东西没看懂),下面就是把vector最后一个指针穿进去,把_val(也就是push_back的东西传进vectro中),然后指针后移,结束push_back,然后我们运行到这个construct函数里面
这个里面又嵌套了一层construct,多加了一个*this参数,这个this是xmemory这个类,内存管理的这个类,我们继续往下
发现这个里面又tm在调用construct,这次是xmemory的construct,把指针和值传进去,我们继续深入。
这次总算是到了最后一步了,这个函数里面调用了一个::new全局的new,这一步里面用到了一个完美转发,完美转发和刚才的通用引用就是为了保持传进来的参数的类型不会改变,大概意思就是传进来的是右值,那么就去调用对象的移动构造函数,反之则调用拷贝构造,如果没有移动构造,那么全都会调用拷贝构造,如果连拷贝构造都没有,那么就调默认的浅拷贝,而且这里面还涉及到了一个容易让人忽视的地方,就是(void *)这个隐式转换所带来的NULL和nullptr的区别的问题(如果感兴趣可以用智能指针用make_shared初始化为0和nullptr看看会有什么不同效果),后面那个push_back对象就会调用拷贝构造了。