引子:在生活中,我们使用vector很频繁,我们是不是可以自己来实现一个呢?,其实也不难,我们在学过数据结构,就很easy啦!
As we all kowmn,vector基础有以下函数!
from my perspective<实现代码
#define _CRT_SECURE_NO_WARNINGS
#pragma once
#include<iostream>
#include<vector>
#include<assert.h>
#include<algorithm>
using namespace std;
namespace My_vector
{
template<class T>
class vector
{
public:
// Vector的迭代器是一个原生指针
typedef T* iterator;//进行统一的命名
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
typedef const T* const_iterator;
const_iterator cbegin() const
{
return _start;
}
const_iterator cend() const
{
return _finish;
}
size_t capacity() const
{
return _end_of_storage - _start;
}
size_t size() const
{
return _finish - _start;
}
void reserve(size_t n)
{
if (n > capacity())
{
size_t oldsize = size();
T* tmp = new T[n];//创建临时变量,利用完后会进行析构
if (_start)//进行拷贝数据
{
for (size_t i = 0; i < oldsize; i++)
{
tmp[i] = _start[i];
}
delete[] _start;
}
_start = tmp;
_finish = _start + oldsize;
_end_of_storage = _start + n;
}
}
void push_back(const T& x)
{
if (_finish == _end_of_storage)//进行扩容操作
{
size_t newcapacity = (_end_of_storage-_start)== 0 ? 4 :(_end_of_storage - _start)*2;
reserve(newcapacity);
}
*_finish = x;
_finish++;
}
void resize(size_t n, const T& value = T())//默认的升序的排序
{
if (n <= size())
{
_finish = _start+n;
}
else
{
for (int i = 0; i < (n - size()); i++)
{
push_back(value);
}
}
}
void swap(vector<T>&v)
{
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_end_of_storage, v._end_of_storage);
}
vector()
: _start(nullptr),
_finish(nullptr),
_end_of_storage(nullptr)
{}
vector(int n, const T& value = T())
: _start(nullptr),
_finish(nullptr),
_end_of_storage(nullptr)
{
reserve(n);
for (int i = 0; i < n; i++)
{
push_back(value);
}
}
template<class InputIterator>
vector(InputIterator first=nullptr, InputIterator last=nullptr)
{
reserve(last - first);
while (first != last)
{
push_back(*first);
++first;
}
}
vector(const vector<T>& v)
: _start(nullptr),
_finish(nullptr),
_end_of_storage(nullptr)
{
reserve(v.capacity());
for (auto e : v)
{
push_back(e);
}
}
// "=default"函数特性仅适用于类的特殊成员函数,且该特殊成员函数没有默认参数
//
// vector():default;
vector<T>& operator=(vector<T> v)
{
swap(v);//交换后,不需要的会被析构掉!
return *this;
}
~vector()
{
if (_start)
{
delete[] _start;
_start = _finish = _end_of_storage = nullptr;
}
}
T& operator[](size_t i)
{
assert(i < size());
return _start[i];
}
const T& operator[](size_t i) const
{
assert(i < size());
return _start[i];
}
void pop_back()
{
assert(size() > 0);
--_finish;
}
void erase(iterator pos)
{
assert(pos >= _start);
assert(pos < _finish);
iterator it = pos + 1;
while (it != _finish)
{
*(it - 1) = *it;
++it;
}
--_finish;
}
//注意迭代器失效问题
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;
}
private:
iterator _start; // 指向数据块的开始
iterator _finish; // 指向有效数据的尾
iterator _end_of_storage; // 指向存储容量的尾
};
void text1()
{
vector<int>c;
c.push_back(1);
c.push_back(2);
c.push_back(3);
c.push_back(14);
c.insert(c.begin() + 2, 29);
c.insert(c.begin() + 3, 99);
c.erase(c.begin());
for (auto e : c)
{
cout << e << " ";
}
}
}
效果:
疑问+解答:
可不可以用输出流呢?答案是可以的!
template<typename T>
ostream& operator<<(ostream& out, const vector<T>& d)
{
typename vector<T>::const_iterator cit = d.cbegin();
while (cit != d.cend())
{
out << *cit << endl;
++cit;
}
//for (auto e : d)
//{
// out << e << " ";
//}
return out;
}
迭代器失效问题
个人认为:失效情况一般发生在增容与缩容上
有以下几种情况
一,会引起其底层空间改变的操作,都有可能是迭代器失效,
比如:resize、reserve、insert、assign、 push_back等,也就是说vector底层原理旧空间被释放掉, 而在打印时,it还使用的是释放之间的旧空间,在对it迭代器操作时,实际操作的是一块已经被释放的 空间,而引起代码运行时崩溃。
解决方式:在以上操作完成之后,如果想要继续通过迭代器操作vector中的元素,只需给it重新 赋值即可。
二,指定位置元素的删除操作--erase
删除pos位置元素后,pos位置之后的元素会往前搬移,没有导致底层空间的改变,理论上讲迭代 器不应该会失效,但是:如果pos刚好是最后一个元素,删完之后pos刚好是end的位置,而end位置是 没有元素的,那么pos就失效了。因此删除vector中任意位置上元素时,vs就认为该位置迭代器失效 了
也就是,原来的迭代器指向的位置变为了它的下一个。
三,与vector类似,string在插入+扩容操作+erase之后,迭代器也会失效
继承
接上回所说!我们讲到继承的默认构造函数!那我们讲讲继承的分类与默认函数!
分类:
一,多层继承是从一个派生类派生出另一个派生类的过程。
二,多重继承是从两个或更多类派生出一个新类的过程
三,混合继承是多种类型继承的组合。
四,分层继承是从一个基类派生出两个或更多个类的过程。
继承的默认函数!
一,默认构造函数
- 如果基类有一个默认构造函数(即无参数的构造函数),并且派生类没有显式地调用基类的构造函数,那么编译器会隐式地调用基类的默认构造函数。
- 如果基类没有默认构造函数,但派生类没有显式地调用基类的任何构造函数,那么编译器会报错,因为它不知道如何初始化基类部分
- 在派生类的构造函数的初始化列表中,可以显式地调用基类的构造函数。
- 总结:不管子类,都会调用父类的!然后才会调用子类
二,c++继承中默认析构函数
-
自动生成:如果派生类没有定义自己的析构函数,编译器会为它生成一个默认析构函数。这个默认析构函数会隐式地调用基类的析构函数(如果存在的话)。
-
清理工作:当派生类对象被销毁时,其析构函数首先被调用。派生类的析构函数体中通常包含释放派生类成员变量所占用的资源的代码。然后,析构函数会自动调用基类的析构函数,以便释放基类部分所占用的资源。这个过程会一直递归进行,直到最顶层的基类。
-
虚析构函数:在涉及多态的情况下(即基类指针或引用可能指向派生类对象时),基类应该声明一个虚析构函数(virtual destructor)。这是因为如果没有虚析构函数,当使用基类指针删除派生类对象时,只有基类的析构函数会被调用,派生类的析构函数不会被调用,这可能导致资源泄露和其他问题。声明虚析构函数可以确保在删除对象时,首先调用派生类的析构函数,然后再调用基类的析构函数。
-
删除或禁用默认析构函数:和构造函数一样,析构函数也可以被声明为
= delete;
来禁用它,或者由于其他原因而变得不可用(如被声明为私有)。如果派生类的析构函数被禁用或不可用,那么该派生类的对象就不能被销毁。
三,c++继承中默认拷贝构造函数
在继承的上下文中,派生类的拷贝构造函数需要特别小心处理,以确保基类部分也被正确地拷贝。
如果派生类没有显式定义拷贝构造函数,编译器会生成一个默认的拷贝构造函数。这个默认的拷贝构造函数会调用基类的拷贝构造函数(如果存在的话)来拷贝基类部分,并逐个成员地拷贝派生类自己的成员。
然而,在某些情况下,你可能需要自定义派生类的拷贝构造函数,以确保正确地拷贝派生类特有的数据成员或执行其他必要的操作。在自定义拷贝构造函数时,你应该显式地调用基类的拷贝构造函数,以确保基类部分也被正确地拷贝。