非常好的例子,有许多需要注意的地方,所以单独写一篇博客
通过动态内存管理实现vector
#ifndef _VECTOR_H_ #define _VECTOR_H_ #include <memory> #include <iostream> #include <string> #include <map> #include <initializer_list> #include <algorithm> class StrVec { friend bool operator==(const StrVec &lhs, const StrVec &rhs); friend bool operator!=(const StrVec &lhs, const StrVec &rhs); friend bool operator<(const StrVec &lhs, const StrVec &rhs); public: StrVec():elements(nullptr), first_free(nullptr), cap(nullptr) { } //使用初始值列表的构造函数,记住initializer_list里面全是常量,且初始值列表含有begin()和end(),可以使用范围for StrVec(const std::initializer_list<std::string>&il); StrVec(const StrVec &sv); StrVec(StrVec &&sv)noexcept; //移动拷贝 StrVec& operator=(const StrVec &sv); StrVec& operator=(StrVec &&sv)noexcept; //移动赋值 StrVec& operator=(const std::initializer_list<std::string>&il); //重载赋值运算符。 ~StrVec(); void push_back(const std::string &s); std::size_t size()const { return first_free - elements; }// const StrVec* const this std::size_t capacity()const { return cap - elements; } std::string* begin()const { return elements; } std::string* end()const { return first_free; } void reserve(std::size_t n); void resize(std::size_t n); private: //工具函数 //设置为static很巧妙,只初始化一次,且在对象分配前就初始化,所有对象共用一个。 static std::allocator<std::string> alloc;//分配元素 void chk_n_alloc() { if(size() == capacity()) reallocate(); } std::pair<std::string*, std::string*>alloc_n_copy (const std::string*b, const std::string*e); void free(); void reallocate(); std::string *elements; //首元素 std::string *first_free; //末元素的下一个位置 std::string *cap; //分配的内存末尾之后的位置 }; #endif
注意!
1.开始使用了c++11 的nullptr而没有使用NULL,原因是NULL是c风格中的东西,它只是一个宏定义,如果我们用在c++中重载函数有时会出现二义性
所以在c++中我们应该使用nullptr。
foo(int) {} foo(char *) {} foo(0) //调用foo(int) foo(NULL) //调用foo(int) foo(nullptr) //调用foo(char *)
2.函数参数列表后面添加const一定是类的成员函数,也就是常量成员函数,因为常量成员函数的const是修饰类对象的this指针的。
3.工具函数,在我们定义的类中,如果有多个成员函数有相同的操作,我们就应该定义工具函数,为了避免代码重复,且工具函数应该是private的
4.类中可声明static对象,static是存储在全局区(静态区),简单理解我们可以认为它不属于类(为了理解),类中的static成员可以是该类所有"实例对象"所共用的
,这样就减少了资源的浪费,像例子中的allocator,完全可以所有的对象共用一个alloc,那么我们把它声明为static类型的即可实现,但是要注意static的成员必须在类内
声明,类外定义,一般在main函数的前面我们把所有的static成员初始化。(const static要在类内初始化)。
5.返回一对可使用pair。
#include "vector.h" void StrVec::push_back(const std::string &s)//必须是const的否则参数为初始值列表的构造函数会出错。 { chk_n_alloc(); alloc.construct(first_free++, s); } std::pair<std::string*, std::string*> StrVec::alloc_n_copy(const std::string *b, const std::string *e) { //重新分配一块内存 auto data = alloc.allocate(e-b); return {data, uninitialized_copy(b,e,data)}; } /* void StrVec::free() { if(elements) { for(auto p = first_free; p != elements;) alloc.destroy(--p); alloc.deallocate(elements, cap - elements); } } */ //不如上面的free好,如果for_each()里面的范围是迭代器,会出现意想不到的问题 void StrVec::free() { if(elements) { std::for_each(elements, first_free, [](std::string &s) { alloc.destroy(&s); }); alloc.deallocate(elements, cap - elements); } } StrVec::StrVec(const StrVec& sv) { auto newdata = alloc_n_copy(sv.begin(), sv.end()); elements = newdata.first; first_free = cap = newdata.second; } StrVec::~StrVec() { free(); } StrVec& StrVec::operator=(const StrVec &sv) { auto data = alloc_n_copy(sv.begin(), sv.end()); free(); elements = data.first; first_free = cap = data.second; return *this; } void StrVec::reallocate() { //auto newcapacity = 2 * size(); error auto newcapacity = size() ? 2 * size() : 1; auto newdata = alloc.allocate(newcapacity); auto dest = newdata; auto elem = elements; //for(; elem != first_free;) error // *dest++ = *elem++; for(std::size_t i = 0; i != size(); ++i) alloc.construct(dest++, std::move(*elem++)); free(); elements = newdata; first_free = dest; cap = elements + newcapacity; } void StrVec::reserve(std::size_t n) { if(n > capacity()) { auto newcapacity = n; auto newdata = alloc.allocate(newcapacity); auto dest = newdata; auto elem = elements; for(std::size_t i = 0; i != size(); ++i) alloc.construct(dest++, std::move(*elem++)); free(); elements = newdata; first_free = dest; cap = elements + n; } } void StrVec::resize(std::size_t n) { std::size_t t = n - size(); if(n < size()) { while(n--) { alloc.destroy(--first_free); } } else if(n > size() && n <= capacity()) { for(std::size_t i = 0; i < t; ++i) { alloc.construct(first_free+i, " "); } } else if(n > capacity()) { auto newcapacity = n; auto newdata = alloc.allocate(newcapacity); auto dest = newdata; auto elem = elements; for(std::size_t i = 0; i != size(); ++i) alloc.construct(dest++, std::move(*elem++)); for(std::size_t i = size(); i <= capacity(); ++i) alloc.construct(dest++, " "); free(); elements = newdata; first_free = newdata+n; cap = newdata+n; } } StrVec::StrVec(const std::initializer_list<std::string>&il): elements(nullptr), first_free(nullptr), cap(nullptr) { for(const std::string p : il) { push_back(p); } } StrVec::StrVec(StrVec &&sv)noexcept { elements = sv.elements; first_free = sv.first_free; cap = sv.cap; sv.elements = sv.first_free = sv.cap = nullptr; //确保移动源处于可销毁状态 } StrVec& StrVec::operator=(StrVec &&sv)noexcept { if(this != &sv) //判断是否自赋值 { free(); elements = sv.elements; first_free = sv.first_free; cap = sv.cap; sv.elements = sv.first_free = sv.cap = nullptr; } return *this; } StrVec& StrVec::operator=(const std::initializer_list<std::string>&il) { auto data = alloc_n_copy(il.begin(), il.end()); //不需要考虑自赋值 free(); elements = data.first; //data是pair first_free = cap = data.second; return *this; } bool operator==(const StrVec &lhs, const StrVec &rhs) { return lhs.elements == rhs.elements && lhs.first_free == rhs.first_free && lhs.cap == rhs.cap; } bool operator!=(const StrVec &lhs, const StrVec &rhs) { return !(lhs == rhs); } bool operator<(const StrVec &lhs, const StrVec &rhs) { auto lhs_address = lhs.elements; auto rhs_address = rhs.elements; while(lhs_address != lhs.first_free && rhs_address != rhs.first_free) { if(*lhs_address < *rhs_address) { return true; } else if(*lhs_address > *rhs_address) { return false; } lhs_address++; rhs_address++; } if(lhs_address != lhs.first_free) { return false; } else { return true; } }
注意:部分代码
void StrVec::free() { if(elements) { //error:std::for_each(elements, first_free, [](std::string *s) { alloc.destroy(s); }); std::for_each(elements, first_free, [](std::string &s) { alloc.destroy(&s); }); alloc.deallocate(elements, cap - elements); } }以前博客里有lambda传递的参数要和处理的对象一致,for_each处理的每一个对象是std::string类型的,所以lambda表达式参数也应该是std::string而不是
std::string*, for_each( )前面的两个指针是用来遍历范围,而不是意味着元素就是std::string*, 分清要处理的类型到底是什么!。
main函数
#include "vector.h" std::allocator<std::string> StrVec::alloc; int main() { StrVec sv; std::string s; while(std::cin >> s && s != "q") { sv.push_back(s); } std::cout << "size:" << sv.size() << " "; std::cout << "capacity:" << sv.capacity() << " "; std::cout << "begin:" << *sv.begin() << " "; std::cout << "end:" << *(sv.end()-1) << std::endl; s = "stop"; sv.push_back(s); sv.push_back(s); std::cout << "size:" << sv.size() << " "; std::cout << "capacity:" << sv.capacity() << " "; std::cout << "begin:" << *sv.begin() << " "; std::cout << "end:" << *(sv.end()-1) << std::endl; sv.reserve(10); std::cout << "reserve:" << std::endl; std::cout << "size:" << sv.size() << " "; std::cout << "capacity:" << sv.capacity() << " "; std::cout << "begin:" << *sv.begin() << " "; std::cout << "end:" << *(sv.end()-1) << std::endl; sv.resize(11); std::cout << "resize:" << std::endl; std::cout << "size:" << sv.size() << " "; std::cout << "capacity:" << sv.capacity() << " "; std::cout << "begin:" << *sv.begin() << " "; std::cout << "end:" << *(sv.end()-1) << std::endl; std::cout << "构造函数初始值列表:" << std::endl; StrVec sv2({"wang","wei","hao"}); std::cout << "size:" << sv2.size() << " "; std::cout << "capacity:" << sv2.capacity() << " "; std::cout << "begin:" << *sv2.begin() << " "; std::cout << "end:" << *(sv2.end()-1) << std::endl; StrVec sv3(sv2); StrVec sv4; sv4 = sv2; std::cout << "拷贝构造:" << std::endl; std::cout << "size:" << sv3.size() << " "; std::cout << "capacity:" << sv3.capacity() << " "; std::cout << "begin:" << *sv3.begin() << " "; std::cout << "end:" << *(sv3.end()-1) << std::endl; std::cout << "赋值构造:" << std::endl; std::cout << "size:" << sv4.size() << " "; std::cout << "capacity:" << sv4.capacity() << " "; std::cout << "begin:" << *sv4.begin() << " "; std::cout << "end:" << *(sv4.end()-1) << std::endl; }
新添加重载操作符== , != , <。