非常好的例子,有许多需要注意的地方,所以单独写一篇博客
通过动态内存管理实现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;
}
新添加重载操作符== , != , <。