现在又如下代码:(自定义的String的类)
class String { public: String(const char *str = nullptr) { cout << "String()" << endl; if (str == nullptr) { _ptr = new char[1]; _ptr = '\0'; } else { _ptr = new char[strlen(str) + 1]; strcpy(_ptr, str); } } ~String() { cout << "~String()" << endl; delete[]_ptr; _ptr = nullptr; } String(const String &obj) { cout << "String(const String &obj)" << endl; _ptr = new char[strlen(obj._ptr) + 1]; strcpy(_ptr, obj._ptr); } String(String &&obj) { cout << "String(const String &&obj)" << endl; this->_ptr = obj._ptr; obj._ptr = nullptr; } String &operator=(const String &obj) { cout << "operator=(const String &obj)" << endl; if (this == &obj) { return *this; } delete[] _ptr; _ptr = new char[strlen(obj._ptr) + 1]; strcpy(_ptr, obj._ptr); } String &operator=(String &&obj) { cout << "operator=(const String &&obj)" << endl; this->_ptr = obj._ptr; obj._ptr = nullptr; return *this; } private: char *_ptr; };main方法的调用
int main() { vector<String> vec; vec.reserve(10); String str1 = "hello"; cout << "---------------------" << endl; vec.push_back(str1); vec.push_back(String("world")); cout << "--------------------------" << endl; return 0; }结果:
那么vector是怎么做到的呢?它怎么知道使用String的左值引用的拷贝构造还是使用String的右值拷贝构造?
我们首先实现自己的vector容器。
//容器空间适配器 template<typename T> struct Allocator { T *allocate(size_t size)//负责内存的开辟 { return (T*)malloc(sizeof(T)*size); } void deallocate(void *p)//负责内存的释放 { free(p); } void construct(T *p, const T &val)//负责对象的构造 { new(p) T(val); } void destroy(T *p)//负责对象的析构 { p->~T(); } }; template<typename T,typename Alloc = Allocator<T>> class vector { public: vector(size_t size = 10) { _first = _allocator.allocate(size);//只给数组开辟空间,不进行构造 _last = _first; _end = _first + size; } ~vector() { T *ptmp = _first; for (; ptmp != _last; ++ptmp)//首先释放数组中的有效元素 { _allocator.destroy(ptmp); } //释放数组 _allocator.deallocate(_first); _first = _end = _last = nullptr; } vector(const vector&src) { //首先申请空间 size_t size = src._end - src._first;//获取src的数组长度 _first = _allocator.allocate(size);//申请空间 _last = _first; int len = src._last - src._first; for (int i = 0; i < len; i++)//拷贝数据 { _allocator.construct(_last++, src._first[i]); } _end = _first + size; } void operator=(const vector&src) { //1,判断是否为自赋值 if (this == &src) return; //2,释放原来对象占用的空间 ~vector(); //3,和拷贝构造过程一样 size_t size = src._end - src._first;//获取src的数组长度 _first = _allocator.allocate(size);//申请空间 _last = _first; int len = src._last - src._first; for (int i = 0; i < len; i++) { _allocator.construct(_last++, src._first[i]); } _end = _first + size; } void push_back(const T &val) { if (full()) expand(); _allocator.construct(_last++, val); } void pop_back() { if (empty()) return; --_last; _allocator.destroy(_last); } T back() { if (empty()) throw "the vector is empty"; return *(_last - 1); } bool full()const { return _last == _end; } bool empty()const { return _first == _last; } size_t size()const { return _last - _first; } private: T *_first;//指向数组的首元素的地址 T *_last;//指向数据最后一个有效元素的后继 T *_end;//指向数组有效空间最后一个元素的后继 Alloc _allocator;//空间适配器 void expand()//二倍扩容 { size_t len = _end - _first;//当前的数组大小 size_t size = len * 2;//扩容之后的数组的大小 T *ptmp = _allocator.allocate(size);//先申请大小为原来二倍的空间 T *pcur = _first;// for (int i = 0; i < len; i++) _allocator.construct(ptmp + i, *pcur++); for (int i = 0; i < len; i++) _allocator.destroy(_first++); _allocator.deallocate(_first); _first = ptmp; _end = _first + size; _last = _first + len; } };我们在vector类里面增加一个带右值引用的push_back的重载函数,
在空间配置器里面增加一个带右值引用的construct的方法。
void push_back(T &&val) { if (full()) expand(); _allocator.construct(_last++, std::move(val)); } void construct(T *p, T &&val)//负责对象的构造 { new(p) T(std::move(val)); }上面用到了方法std::move()移动语义。。。由于我们知道,右值引用本身是一个左值,所以我们在传参的时候,希望使用它的本身的右值语义,我们这里使用move进行了移动语义将val从左值引用转化位右值引用类型。
测试结果:
但是上面的方法不免显得有点繁琐了,因为一旦我们要使用val的右值引用就必须给它类型转化。
我们可以使用函数模板来简化我们的代码量。
template<typename Ty> void push_back(Ty &&val) { if (full()) expand(); _allocator.construct(_last++, std::forward<Ty>(val)); }std::forward(Ty)(val)自动识别val是左值还是右值。
根据函数模板参数推演+引用折叠我们可以使用这个方法很好的解决右值引用本身是左值的问题。
引用折叠: 右值引用+右值引用=右值引用
右值引用+左值引用=左值引用
同理我们的空间配置也可以这样实现
template<typename Ty> void construct(T *p, Ty &&val)//负责对象的构造 { new(p) T(std::forward<Ty>(val)); }