做门户网站用什么模板好,做鼻翼整形整形的网站,大连模板做网站,池州有哪些做网站的#x1f525;博客主页#xff1a;小王又困了
#x1f4da;系列专栏#xff1a;C
#x1f31f;人之为学#xff0c;不日近则日退
❤️感谢大家点赞#x1f44d;收藏⭐评论✍️ 目录
一、存储结构
二、默认成员函数
#x1f4d2;2.1构造函数
#x1f4d2;2.2拷贝…
博客主页小王又困了
系列专栏C
人之为学不日近则日退
❤️感谢大家点赞收藏⭐评论✍️ 目录
一、存储结构
二、默认成员函数
2.1构造函数
2.2拷贝构造
2.3赋值重载
三、容量操作
3.1获取有效元素个数
3.2获取空间容量大小
3.3使用reverse扩容
四、数据访问
4.1下标访问
4.1迭代器访问 五、修正操作
5.1尾插数据
5.2尾删数据
5.3在任意位置插入数据
5.4在任意位置删除数据 ️前言 vector是STL容器之一其底层实现类似于数据结构顺序表相当于string来说得益于泛型模板的加持使得vector可以变为任何类型且是可以动态扩容。接下来我们就通过vector各种接口的模拟实现来深入了解vector。 一、存储结构 与string底层结构不同vector底层空间结构为三个指针:
namespace bit
{templateclass T //模板参数Tclass vector{public:typedef T* iterator; //指针重命名为迭代器typedef const T* const_iterator;private:iterator _start; iterator _finish; iterator _end_of_storage; }
}
_start指向空间的起始地址_finish指向最后一个数据的下一个地址相当于_size_end_of_stroage指向空间最后一个最末地址,相当于_capacity
小Tips由于vector使用了模板所以函数实现都在头文件中防止因为模板导致的链接错误的问题
二、默认成员函数
2.1构造函数 我们在这里实现三个版本分别是默认构造函数带参构造函数和迭代器区间构造。
默认构造函数初始化三个指针置空即可带参构造函数初始化n个T类型的value值在对象中迭代器区间构造通过其他容器迭代器或指针迭代插入其所有值
//默认构造函数
vector():_start(nullptr), _finish(nullptr), _end_of_storage(nullptr)
{}vector () default //强制编译器生存默认构造//构造n个T类型数据
vector(size_t n, const T value T()) :_start(nullptr), _finish(nullptr), _end_of_storage(nullptr)
{reserve(n);for (int i 0; i n; i){*(_finish) value;}
}//迭代器区间构造
templateclass InputIterator
vector(InputIterator first, InputIterator last):_start(nullptr), _finish(nullptr), _end_of_storage(nullptr)
{while (first ! last) //迭代器插入数据{push_back(*first);first;}
}
注意我们实现了带参构造函数的n为size_t类型的版本如果我们使用带参构造函数实例化则会发生非法间接寻址的错误这是因为size_t是整型实例化T数据类型也是整型此时编译器会自动匹配最合适的构造函数于是匹配到了迭代器区间构造。我们只要写一个n为int类型的带参构造参数去匹配就可以解决问题。
2.2拷贝构造 对于拷贝构造我们要考虑深浅拷贝的问题我们希望当一个vector对象拷贝另一个对象时新对象开辟新的空间拷贝数据而不是两个对象共用同一块空间否则会析构两次造成内存泄漏。
vector(const vectorT v)
{reserve(v.capacity());for (auto e : v){push_back(e);}
}2.3赋值重载 赋值重载与拷贝构造的问题类似也要注意深拷贝问题区别于拷贝构造的地方在于不需要新建对象但是需要判断是否为同一个对象避免重复开空间。
//传统写法
vectorT operator(const vectorT v)
{if (v ! this) {clear(); reserve(v.capacity()); for (int i 0; i v.size(); i){*(_finish) v._start[i]; }}return *this;
}//现代写法
void swap(vectorT v)
{std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_end_of_storage, v._end_of_storage);
}// v1 v3
vectorT operator(vectorT v)
{swap(v);return *this;
}
三、容量操作
3.1获取有效元素个数
size_t size() const
{return _finish - _start;
}
3.2获取空间容量大小
size_t capacity() const
{return _end_of_storage - _start;
} 小Tipsvevtor是使用3个指针对空间进行管理使用_finish(有效数据指针)减去_start(空间首地址)就能得出有效数据个数容量同理。我们只是对数据进行访问不涉及修改所以使用const修饰this指针。
3.3使用reverse扩容
void reserve(size_t n)
{if (n capacity()){size_t oldsize size();//获取扩容前有效数据个数T* tmp new T[n];if(_start){//memcpy(tmp, _start, sizeof(T*) * size());for (size_t i 0; i oldsize; i){tmp[i] _start[i];}delete[] _start;}_start tmp;_finish _start oldsize;_end_of_storage _start n;}
}
//错误写法_finish _start size()
小Tips我们要用oldsize记录扩容前有效数据的个数按照错误写法size()是用扩容前的_finish和扩容后的_start来计算的结果是错误的。当string类型的对象要扩容时memcpy对数据进行的是浅拷贝两个对象指向同一块空间会析构两次造成内存泄漏。
四、数据访问
4.1下标访问 下标访问是通过重载 [ ] 运算符实现的在下标pos正确的情况下返回当前下标字符的引用否则assert报错。
T operator[](size_t pos)
{assert(pos size());return _start[pos];
}const T operator[](size_t pos) const //const引用版本,不可以修改
{assert(pos size()); return _start[pos];
}at 函数我们可以复用运算符[ ]来实现。 at 函数的检查手段是抛异常。
T at(size_t pos)
{ return (*this)[pos];
}const T at(size_t pos) const
{ return (*this)[pos];
}4.1迭代器访问
typedef T* iterator;
typedef const T* const_iterator;const_iterator begin() const { return _start; }
const_iterator end() const { return _finish; }iterator begin() { return _start; }
iterator end() { return _finish; }//范围for
for (auto e : v1)
{cout e ;
}
cout endl; 五、修正操作
5.1尾插数据 在尾插前检查容量是否充足空间不够扩容然后直接插入_finish的空位下即可_finish指针移动到下一个空位。
void push_back(const T x)
{if (_finish _end_of_storage){size_t newcapacity capacity() 0 ? 4 : capacity() * 2;reserve(newcapacity);}*_finish x;_finish;//insert(end(), x); //复用
}
5.2尾删数据 尾删只需要--_finish但需要判断size是否0。
void pop_back()
{assert(size() 0);--_finish;
}
5.3在任意位置插入数据 当我们传递一个迭代器在pos位置插入数据时可能涉及容器扩容如果扩容那么迭代器是旧空间的迭代器则会导致迭代器失效因为原有空间已经被释放但迭代器还是指向原空间(那么就是野指针)所以我们在插入或删除后要更新迭代器。
iterator insert(iterator pos, const T x)
{assert(pos _start);assert(pos _finish);if (_finish _end_of_storage){size_t len pos - _start;size_t newcapacity capacity() 0 ? 4 : capacity() * 2;reserve(newcapacity);pos _start len;}iterator end _finish - 1;while (end pos){*(end 1) *end;--end;}*pos x;_finish;return pos;
}
小Tips如果要进行扩容我们用len来记录扩容前从_start到pos位置数据的个数然后扩容后更新pos的位置。
5.4在任意位置删除数据 删除元素也会有迭代器失效的问题可能会越界访问 。
iterator earse(iterator pos)
{assert(pos _start);assert(pos _finish);iterator it pos 1;while (it ! _finish){*(it - 1) *it;it;}--_finish;return pos; //返回删除元素位置的下一个元素的迭代器
} 结语 本次的内容到这里就结束啦。希望大家阅读完可以有所收获同时也感谢各位读者三连支持。文章有问题可以在评论区留言博主一定认真认真修改以后写出更好的文章。你们的支持就是博主最大的动力。