C++——vector模拟实现(下)

简介: 笔记

构造


1.png2.png3.png

这里默认的拷贝构造是浅拷贝,程序结束会析构俩次,所以崩溃

我们这需要自己设置一个深拷贝

传统写法:

  vector(const vector<T>& v)
    {
      _start = new T[v.size()];
      memcpy(_start, v._start, sizeof(T) * v.size());
      _finish = _start + v.size();
      _end_of_storage = _start + v.size();
    }

4.png

也可这样写

vector(const vector<T>& v)
      :_start(nullptr),
      _finish(nullptr),
      _end_of_storage(nullptr)
    {
      reserve(v.size());
      for (const auto& e : v)
      {
        push_back(e);
      }
    }

现代写法:

vector里面有一个构造是支持迭代器区间构造5.png

之前我们已经有了类模板

image.pngimage.png

现在我们在类模板,成员函数里面再加一个模板


也就是说现在的这个拷贝构造函数里面可以用双重模板,在定义模板是因为我们需要一个迭代区间,而之前我们已经有了iterator,现在是InputIterator,这样写代表我们不仅可以用T类型模板,iterator迭代器,也可以用其他模板和其他迭代器,这样比较灵活


便于我们实现这种情况


image.pngimage.png

当前函数写这样运行的时候会直接报错,因为如果编译器没有对s进行初始化,push_back要调用reverse,删除旧数组空间的时候会报错

 因此我们要对s进行初始化

image.png

template <class InputIterator>
    vector(InputIterator first, InputIterator last)
      : _start(nullptr)
      , _finish(nullptr)
      , _end_of_storage(nullptr)
    {
      while (first != last)
      {
        push_back(*first);
        ++first;
      }
    }

11.png

现代写法:

  template <class InputIterator>
    vector(InputIterator first, InputIterator last)
      : _start(nullptr)
      , _finish(nullptr)
      , _end_of_storage(nullptr)
    {
      while (first != last)
      {
        push_back(*first);
        ++first;
      }
    }
    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(const vector<T>& v)
      :_start(nullptr)
      , _finish(nullptr)
      , _end_of_storage(nullptr)
    {
      vector<T> tmp(v.begin(), v.end());
      swap(tmp);
    }
  vector<T>& operator=(vector<T> v)
    {
      swap(v);
      return *this;
    }

这里传参的时候不进行引用传参,他会调用拷贝构造,根据我们写的拷贝可知,实参会被删除,保留形参,v就是保留下来的形参,然后跟一交换即可,这样比较方便

思考题


12.png

 vector(size_t n, const T& val = T())  插入n个val,val是T类型


使用N个vakue去构造,T()是T类型的匿名对象,匿名对象就要调用默认构造,如果是Date这些自定义类型,就需要我们人为的把构造函数写好,如果T是int,int也有自己的默认构造,内置类型也有自己的默认构造

vector(size_t n, const T& val = T())
      :_start(nullptr)
      ,_finish(nullptr)
      ,_end_of_storage(nullptr)
    {
      reserve(n);
      for (int i = 0; i < n; i++)
      {
        push_back(val);
      }
    }


13.png14.png

当这样初始化会报错

15.png

报错信息在这一行:非法的间接寻址

image.png

当编译器进行类型匹配的时候,如果只传了一个参数,编译器会把那个参数传给最左边的形参(前面博客有说过)

而传俩个参数都是int类型,编译器在进行类型匹配的时候会找最匹配的类型来匹配

一个参数是unsigned int,一个是int


image.png

对于这里传参传的是int,int,这个对拷贝构造来说特别匹配,first 和last都是int,然后对int解引用就报错

image.png

而下面这个不报错因为,对于拷贝构造来说不匹配,所以它进不去拷贝构造

image.png

resize


void resize(size_t n, const T& val = T())
    {
      if (n > capacity())
      {
        reserve(n);
      }
      if (n > _finish)
      {
        while (_finish < _start + n)
        {
          *finish = val;
          ++_finish;
        }
      }
      else
      {
        _finish = _start + n;
      }
    }

reserve对_finish不是很敏感,resize可以改变_finish


vector<vevtor<T>>深浅拷贝问题 
  vector(const vector<T>& v) //拷贝构造函数
  {
    _start = new T[v.size()];
    memcpy(_start, v._start, sizeof(T) * v.size());
    _finish = _start + v.size();
    _end_of_storage = _start + v.size();
  }
class Solution {
  public:
  vector<vector<int>> generate(int numRows) {
    vector<vector<int>> vv;
    vv.resize(numRows);
    for (int i = 0; i < vv.size(); ++i)
    {
    vv[i].resize(i + 1, 0);
    vv[i].front() = vv[i].back() = 1;
    }
    for (size_t i = 0; i < vv.size(); ++i)
    {
    for (size_t j = 0; j < vv[i].size(); ++j)
    {
      if (vv[i][j] == 0)
      {
      vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1];
      }
    }
    }
    return vv;
  }
  };
  void test_vector1()
  {
  Solution().generate(5);
  }

上面代码是拷贝构造函数以及杨辉三角的调用


程序直接崩溃image.png

这是因为在函数返回的时候要调用一次拷贝构造,而该拷贝构造对外面是深拷贝,对里面是浅拷贝

21.pngimage.png

这种拷贝方式只是对最外层完成了慎深拷贝,而对于里层确实浅拷贝

image.png

问题出在了memcpy上,memcpy是一个字节一个字节往过拷贝,导致这里里层的_start指向同一块空间,析构的时候会析构俩次,因此这里不能用memcpy


这样修改拷贝构造,程序即可正常运行


vector(const vector<T>& v)
  {
    _start = new T[v.size()];
  //  memcpy(_start, v._start, sizeof(T) * v.size());
    for (int i = 0; i < v.size(); ++i)
    {
    _start[i] = v._start[i];
    }
    _finish = _start + v.size();
    _end_of_storage = _start + v.size();
  }

防止reserve出现这种问题,对reserve也可进行修改


void reserve(size_t n)
  {
    if (n > capacity())
    {
    size_t sz = size();
    T* tmp = new T[n];
    if (_start)
    {
      //memcpy(tmp, _start, sizeof(T) * sz);
      for (size_t i = 0; i < sz; ++i)
      {
      tmp[i] = _start[i];
      }
      delete[] _start;
    }
    _start = tmp;
    _finish = _start + sz;
    _end_of_storage = _start + n;
    }
  }

习题 电话号码的字母组合


17. 电话号码的字母组合 - 力扣(LeetCode)

image.png

class Solution {
    char *numToStr[10]={"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
public:
void Combine(string digits,int di,vector<string>& retv,string CombinStr)
{
    if(di==digits.size())
    {
        retv.push_back(CombinStr);
        return ;
    }
    int num=digits[di]-'0';
    string str=numToStr[num];
    for(auto ch:str)
    {
        Combine(digits,di+1,retv,CombinStr+ch);
    }
}
    vector<string> letterCombinations(string digits) {
 vector<string> v;
  if(digits.empty())
 return v;
 string str;
 Combine(digits,0,v,str);
 return v;
    }
};

str是一个用来临时存储组合的结果,digits是用户输入的数字,di是下标,如digits是“238”,di=0,就代表表示字符2,所以要给-'0',把每个按键对应的字母存到一个数组中,通过下标来访问


习题 删除有序数组中的重复项


26. 删除有序数组中的重复项 - 力扣(LeetCode)

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
int src=0;
int dst=0;
while(src<nums.size())
{
    if(nums[src]==nums[dst])
    {
        ++src;
    }
    else
    {
   ++dst;
        nums[dst]=nums[src];
        src++;
    }
}
return dst+1;
    }
};
相关文章
|
28天前
|
存储 C++ 容器
【C++】vector的底层剖析以及模拟实现
【C++】vector的底层剖析以及模拟实现
|
1月前
|
存储 算法 测试技术
C++:Vector的使用
C++:Vector的使用
|
1月前
|
存储 算法 C++
【C/C++ Vector容量调整】理解C++ Vector:Reserve与Resize的区别与应用
【C/C++ Vector容量调整】理解C++ Vector:Reserve与Resize的区别与应用
51 1
|
1月前
|
存储 C语言 C++
C++初阶--自我实现vector
C++初阶--自我实现vector
|
1月前
|
程序员 C++ 索引
在C++语言中Vector的命名空间的作用
在C++语言中Vector的命名空间的作用
15 0
|
1月前
|
C++ 容器
vector容器-插入和删除c++的讲解要
vector容器-插入和删除c++的讲解要
17 1
vector容器-插入和删除c++的讲解要
|
1月前
|
存储 C++ 容器
c++vector容器-赋直操作讲解
c++vector容器-赋直操作讲解
34 0
|
1月前
|
C++ 容器
vector容器-插入和删除c++
vector容器-插入和删除c++
25 0
|
1月前
|
存储 C++ 容器
vector容器-容量和大N小c++的讲解
vector容器-容量和大N小c++的讲解
18 1
|
2天前
|
存储 缓存 编译器
【C++进阶(五)】STL大法--list模拟实现以及list和vector的对比
【C++进阶(五)】STL大法--list模拟实现以及list和vector的对比

热门文章

最新文章