【C++】STL - vector使用和模拟实现

news2024/12/23 5:37:47

🐱作者:傻响
🐱专栏:《C/C++ - STL》
🔥格言:你只管努力,剩下的交给时间!

                                                              

目录

No.1.vector的介绍及使用

1.1 vector的介绍

1.2 vector的接口

vector的构造函数接口

vector的析构函数接口

vector的operator函数接口

vector的迭代器函数接口

vector 查看容量的函数接口

vector 元素访问函数接口

vector 的迭代器函数接口

vector的插入函数接口

No.2 vector接口的使用

2.1 在末尾添加元素

2.2 插入数据

2.3 删除数据

2.4 请求改变容量

2.5 元素访问

No.3 vector接口的模拟实现

No.1 实现测试一:

3.1.1 构造函数的模拟实现

No.2 实现测试二:

3.2.1 尾插模拟实现

3.2.2 扩容实现

3.2.3 调整数据大小

No.3 实现测试三:

3.3.1 打印函数的实现

3.3.2 迭代器

3.3.3 operator[]取位置数据

No.4 实现测试四:

3.4.1 指定位置插入数据函数实现

3.4.2 指定位置删除数据函数实现

No.5 赋值函数的实现:

3.5.1 operator=赋值实现

3.5.2 拷贝构造实现

No.6 其他函数实现:

3.6.1 判断容器是否为空

练习题验证


No.1.vector的介绍及使用

1.1 vector的介绍

  1. vector是表示可变大小数组的序列容器。

  2. 就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素 进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自 动处理。

  3. 本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小 为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是 一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大 小。

  4. vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存 储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是 对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。

  5. 因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增 长。

  6. 与其它动态序列容器相比(deque, list and forward_list), vector在访问元素的时候更加高效,在末 尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起list和forward_list 统一的迭代器和引用更好。 使用STL的三个境界:能用,明理,能扩展 ,那么下面学习vector,我们也是按照这个方法去学习

  7. 详细的STL文档请参考:https://cplusplus.com/reference/vector/vector/

1.2 vector的接口

vector的构造函数接口

函数接口函数说明
explicit vector (const allocator_type& alloc = allocator_type());构造函数1,构造一个向量,根据使用的构造函数版本初始化其内容:
explicit vector (size_type n, const value_type& val = value_type(), const allocator_type& alloc = allocator_type());构造函数2,构造一个向量,根据使用的构造函数版本初始化其内容:
template <class InputIterator> vector (InputIterator first, InputIterator last, const allocator_type& alloc = allocator_type());构造函数3,构造一个向量,根据使用的构造函数版本初始化其内容:
vector (const vector& x);拷贝构造

vector的析构函数接口

函数接口函数说明
~vector();销毁容器对象。

vector的operator函数接口

函数接口函数说明
vector& operator= (const vector& x);将新内容分配给容器,替换其当前内容,并相应地修改其大小。

vector的迭代器函数接口

函数接口函数说明
iterator begin();const_iterator begin() const;返回指向vector中第一个元素的迭代器。
iterator end();const_iterator end() const;返回指向vector容器中倒数元素的迭代器。
reverse_iterator rbegin(); const_reverse_iterator rbegin() const;返回指向vector中最后一个元素的反向迭代器(反向迭代器)
reverse_iterator rend(); const_reverse_iterator rend() const;返回一个反向迭代器,指向vector中第一个元素之前的理论元素(反向迭代器)
const_iterator cbegin() const noexcept;返回指向容器中第一个元素的
const_iterator cend() const noexcept;返回一个const_iterator对象,指向容器的末尾元素。
const_reverse_iterator crbegin() const noexcept;返回const_reverse_iterator来反转开头 ,返回指向容器中最后一个元素的(反向迭代器)
const_reverse_iterator crend() const noexcept;返回一个const_reverse_iterator,指向容器中第一个元素之前的理论元素

vector 查看容量的函数接口

函数接口函数说明
size_type size() const;返回大小
size_type max_size() const;返回最大大小
bool empty() const;测试vector是否为空
void resize (size_type n, value_type val = value_type());改变大小
void reserve (size_type n);请求改变容量
size_type capacity() const;返回已分配存储的大小
void shrink_to_fit();缩小到合适的空间

vector 元素访问函数接口

函数接口函数说明
reference operator[] (size_type n); const_reference operator[] (size_type n) const;访问元素
reference at (size_type n); const_reference at (size_type n) const;访问元素
reference front(); const_reference front() const;访问第一个元素
reference back(); const_reference back() const;访问最后一个元素
value_type* data() noexcept;const value_type* data() const noexcept;访问数据:返回指向vector容器内部用于存储其拥有元素的内存数组的直接指针。

vector 的迭代器函数接口

函数接口函数说明
begin + end(重点)获取第一个数据位置的iterator/const_iterator, 获取最后一个数据的下一个位置的iterator/const_iterator
rbegin + rend获取最后一个数据位置的reverse_iterator,获取第一个数据前一个位置的reverse_iterator
iterator begin(); const_iterator begin() const;
iterator end(); const_iterator end() const;
reverse_iterator rbegin(); const_reverse_iterator rbegin() const;
reverse_iterator rend(); const_reverse_iterator rend() const;

vector的插入函数接口

函数接口函数说明
void push_back (const value_type& val);在末尾添加元素
void pop_back();在末尾删除元素
template <class InputIterator> void assign (InputIterator first, InputIterator last); void assign (InputIterator first, InputIterator last);修改vector的内容
iterator insert (iterator position, const value_type& val); void insert (iterator position, size_type n, const value_type& val); template <class InputIterator> void insert (iterator position, InputIterator first, InputIterator last);插入pos位置数据
iterator erase (iterator position); iterator erase (iterator position);删除pos位置数据
void swap (vector& x);交换
void clear();清除容器
template <class... Args>iterator emplace (const_iterator position, Args&&... args);构造和插入元素
template <class... Args> void emplace_back (Args&&... args);构造并在末尾插入元素

No.2 vector接口的使用

我们之前做过C语言的顺序表,现在我们使用一下C++的顺序表,先来看第一个函数

2.1 在末尾添加元素

void push_back (const value_type& val); 在末尾添加元素

描述:

在向量的最后一个元素之后添加一个新元素。val的内容被复制(或移动)到新元素中。容器大小增加1,当且仅当新的向量大小超过当前向量容量时,将自动重新分配已分配的存储空间(扩容)。

// 定义Vector.
vector<int> iVector;
​
// 插入数据
iVector.push_back(1);
iVector.push_back(2);
iVector.push_back(3);
iVector.push_back(4);
iVector.push_back(5);

这里我们想一下Vector的打印方式是如何打印的呢?是不是和String一样呢?

// Vector的打印.
// No.1: 暴力打印.
for (size_t i = 0; i < iVector.size(); i++)
{
    cout << iVector[i] << " ";
}
cout << endl;
// No.2:范围for打印.
for (auto v : iVector)
{
    cout << v << " ";
}
cout << endl;
// No.3: 使用迭代器.
// begin()返回初始迭代器
// end()  返回迭代器结束
vector<int>::iterator itBegin = iVector.begin();
while (itBegin < iVector.end())
{
    cout << *itBegin << " ";
    ++itBegin;
}
cout << endl;
// No.4: 使用反向迭代器.
vector<int>::reverse_iterator ritBegin = iVector.rbegin();
while (ritBegin < iVector.rend())
{
    cout << *ritBegin << " ";
    ++ritBegin;
}
cout << endl;

2.2 插入数据

vector不提供头插,可以直接用insert进行插入数据。

// Vector的打印.
// No.1: 暴力打印.
for (size_t i = 0; i < iVector.size(); i++)
{
    cout << iVector[i] << " ";
}
cout << endl;
// No.2:范围for打印.
for (auto v : iVector)
{
    cout << v << " ";
}
cout << endl;
// No.3: 使用迭代器.
// begin()返回初始迭代器
// end()  返回迭代器结束
vector<int>::iterator itBegin = iVector.begin();
while (itBegin < iVector.end())
{
    cout << *itBegin << " ";
    ++itBegin;
}
cout << endl;
// No.4: 使用反向迭代器.
vector<int>::reverse_iterator ritBegin = iVector.rbegin();
while (ritBegin < iVector.rend())
{
    cout << *ritBegin << " ";
    ++ritBegin;
}
cout << endl;

2.3 删除数据

删除数据和插入数据一样,同样没有头删,可以利用迭代器。

删除数据,也可以指定删除一段区间。

vector<int> iVector;
iVector.push_back(1);
iVector.push_back(2);
iVector.push_back(3);
iVector.push_back(4);
iVector.push_back(5);
for (size_t i = 0; i < iVector.size(); i++)
{
    cout << iVector[i] << " ";  // 1、2、3、4、5
}
cout << endl;
​
iVector.insert(iVector.begin(), 100);
for (auto v : iVector)
{
    cout << v << " ";           // 100、1、2、3、4、5
}
cout << endl;
​
vector<int>::iterator findPos = find(iVector.begin(), iVector.end(), 1);
iVector.insert(findPos, 200);
for (auto v : iVector)
{
    cout << v << " ";           // 100、200、1、2、3、4、5
}
cout << endl;
​
// 尾删.....
iVector.pop_back();
for (auto v : iVector)
{
    cout << v << " ";           // 100、200、1、2、3、4
}
cout << endl;
​
findPos = find(iVector.begin(), iVector.end(), 200);
iVector.erase(findPos);
for (auto v : iVector)
{
    cout << v << " ";           // 100、1、2、3、4
}
cout << endl;
​
​
// 删指定位置的数据...
iVector.erase(++iVector.begin(), --iVector.end());
for (auto v : iVector)
{
    cout << v << " ";           // 100、4
}
cout << endl;

2.4 请求改变容量

void reserve (size_type n); .// 请求改变容量

描述:

要求向量容量至少足以包含n个元素。如果n大于当前的向量容量,函数将使容器重新分配其存储空间,将其容量增加到n(或更大)。在所有其他情况下,函数调用不会导致重新分配,向量容量也不受影响。这个函数对向量的大小没有影响,也不能改变它的元素

void resize (size_type n, value_type val = value_type()); // 改变大小

描述:

调整容器的大小,使其包含n个元素。如果n小于当前容器大小,则内容将减少到其前n个元素,删除超出的元素(并销毁它们)。如果n大于当前容器的大小,则内容将被扩展,在末尾插入所需的元素,以达到n的大小。如果指定了val,则将新元素初始化为val的副本,否则将对它们进行值初始化。如果n也大于当前容器容量,则自动重新分配已分配的存储空间。注意,此函数通过插入或删除容器中的元素来更改容器的实际内容。

// 定义Vector.
vector<int> iVector;
​
// 插入数据
iVector.push_back(1);
iVector.push_back(2);
iVector.push_back(3);
iVector.push_back(4);
iVector.push_back(5);
cout << iVector.capacity() << endl;
​
// 扩容 -> 扩容
iVector.reserve(100);
cout << iVector.capacity() << endl;
// 缩容 -> 不会缩容
iVector.reserve(5);
cout << iVector.capacity() << endl;
// 使用resize()扩容缩容
iVector.resize(8);      // 1、2、3、4、5、0、0、0
cout << iVector.capacity() << endl;
iVector.resize(15, 1);  // 1、2、3、4、5、0、0、0、1、1、1、1、1、1...
cout << iVector.capacity() << endl;
iVector.resize(3);      // 1、2、3
cout << iVector.capacity() << endl;
Vs下的扩容机制

// 测试vector的默认扩容机制
void TestVectorExpand()
{
    vector<int> iVector;
    size_t capacitySize = iVector.capacity();
    
    cout << "making iVector grow:\n";
    for (size_t i = 0; i < 100; ++i)
    {
        iVector.push_back(1);
        if (capacitySize != iVector.capacity())
        {
            cout << "capacity changed: " << capacitySize << endl;
            capacitySize = iVector.capacity();
        }
    }
}
​
/* making iVector grow:
capacity changed: 0
capacity changed: 1
capacity changed: 2
capacity changed: 3
capacity changed: 4
capacity changed: 6
capacity changed: 9
capacity changed: 13
capacity changed: 19
capacity changed: 28
capacity changed: 42
capacity changed: 63
capacity changed: 94 */                   // 1.5倍
Linux下的扩容机制

// 测试vector的默认扩容机制
void TestVectorExpand()
{
    vector<int> iVector;
    size_t capacitySize = iVector.capacity();
    
    cout << "making iVector grow:\n";
    for (size_t i = 0; i < 100; ++i)
    {
        iVector.push_back(1);
        if (capacitySize != iVector.capacity())
        {
            cout << "capacity changed: " << capacitySize << endl;
            capacitySize = iVector.capacity();
        }
    }
}
​
/* making iVector grow:
capacity changed: 0
capacity changed: 1
capacity changed: 2
capacity changed: 4
capacity changed: 8
capacity changed: 16
capacity changed: 32
capacity changed: 64 */                 // 2倍

2.5 元素访问

operator[]与at的区别。

vector<int> iVector;
iVector.reserve(10);
​
for (size_t i = 0; i < 10; i++)
{
    cout << iVector[i] << endl;     // 断言报错。
    
}
​
for (size_t i = 0; i < 10; i++)
{
    cout << iVector.at(i) << endl;  // 抛出异常。
}

assign()。

template <class InputIterator>

void assign (InputIterator first, InputIterator last);

void assign (InputIterator first, InputIterator last);

指定向量内容,将新内容赋给向量,替换其当前内容,并相应地修改其大小。

调用之前容器中保存的任何元素都将被销毁并替换为新构造的元素(不发生元素赋值)。 当且仅当新的向量大小超过当前向量容量时,这将自动重新分配已分配的存储空间

vector<int> iVector;
iVector.push_back(1);
iVector.push_back(2);
iVector.push_back(3);
iVector.push_back(4);
iVector.push_back(5);
​
for (auto v : iVector)
{
    cout << v << endl;  // 1、2、3、4、5
}
​
iVector.assign(10, 20);
for (auto v : iVector)
{
    cout << v << endl;  // 20、20、20、20、20.....
}

assign也可以使用迭代器进行操作。

vector<int> iVector;
iVector.push_back(1);
iVector.push_back(2);
iVector.push_back(3);
iVector.push_back(4);
iVector.push_back(5);
​
for (auto v : iVector)
{
    cout << v << " ";   // 1、2、3、4、5
}
cout << endl;
​
vector<int> iVectorT;
iVectorT.assign(iVector.begin(), iVector.end());
for (auto v : iVector)
{
    cout << v << " ";   // 1、2、3、4、5
}
cout << endl;
​
string str = "ShaXiang";
vector<char> cVector;
cVector.assign(str.begin(), str.end());
for (auto v : cVector)
{
    cout << v << " ";   // S、h、a、X、i、a、n、g
}
cout << endl;

No.3 vector接口的模拟实现

No.1 实现测试一:

模拟实现方式为个人练习所用,理解底层的实现方式:由于和STL的官方库会冲突,所以代码我们卸载Vector.h中的ShaXiang命名空间下,这样方便测试,也不会和标准的官方库发生冲突。

#include<iostream>
#include<cassert>           // ...这里后面还会增加头文件,先用着...
using namespace std;        // 官方标准stl库
​
// 模拟实现Vector容器的命名空间。
namespace ShaXiang
{
    template<class T>
    class Vecitor
    {
    public:
        // 泛型《typedef》指针。
        typedef T* iterator;
​
​
​
    private:
        iterator _start;            // 存储指针
        iterator _finish;           // 存储大小指针
        iterator _endofstorage;     // 容量指针
    };
​
}

3.1.1 构造函数的模拟实现

/* 无参构造函数 */
// Vector()
Vector()
    :_start(nullptr)
    ,_finish(nullptr)
    ,_endofstorage(nullptr)
{
    // ..
}
/* 使用迭代器的构造函数 */
template <class inputIterator>
Vector(inputIterator begin, inputIterator end)
	:_start(nullptr)
	,_finish(nullptr)
	,_endofstorage(nullptr)
{
	while (begin < end)
	{
		pushBack(*begin);
		++begin;
	}
}
/* 使用迭代器的构造函数 */
Vector(int n, const T& val = T())
    :_start(nullptr)
    , _finish(nullptr)
    , _endofstorage(nullptr)
{
    // 先开辟指定的空间,虽然并不能提高多少效率。
    Reserve(n);
    for (int i = 0; i < n; ++i)
    {
        PushBack(val);
    }
}

No.2 实现测试二:

3.2.1 尾插模拟实现

先做出尾插函数方便后面测试,要不然都测试不效果..............我这里直接测试我已经写好的,里面相关的代码这一块都会进行解析,如果看不明白就继续往下看:

/* 尾插 */
// void push_back (const value_type& val);
void PushBack(const T& val)
{
    // 检查扩容.
    if (_finish == _endofstorage)
    {
        // 扩容:我们不知道要扩容多少,这里我们写一个判断:
        size_t newCapacity = Capacity() == 0 ? 4 : Capacity() * 2;
        Reserve(newCapacity);
    }
​
    // 程序走到这里说明:扩容完成,即扩容成不成功在Reserve中进行判断。
    // 直接插入到_finish位置数据,让_finish++
    *_finish = val;
    ++_finish;
}

 

3.2.2 扩容实现

vector的扩容是一个很让人烦的函数,我们这个函数会实现好几遍:因为随着程序不断加大功能,普通的扩容是不能满足当前的需求的,这里会涉及到深浅拷贝的问题:当然代码就是最后一个版本的函数~动动手往下翻翻哦

// 版本一:
/* 扩容 */
void Reserve(size_t n)
{
    if (n > Capacity())
    {
        // No.1 先开辟新的空间.
        T* temp = new T[n];
​
        // 如果是空的容器,不需要进行拷贝,直接使用指针就可以啦
        if (_start != nullptr)
        {
            // No.2 将原有的数据拷贝到新的内存空间中.
            memcpy(temp, _start, sizeof(T) * Size());
            // No.3 销毁掉之前的空间.
            delete[] _start;
        }
        // No.4 使用新的空间
        _start = temp;
        _finish = Size();
        _endofstorage = _start + n;
    }
    
}
// 这个版本的程序我们直接运行会发现系统出现了访问错误的问题:那我们看看究竟是怎么回事:

 我对代码进行了调试,这里我观察到_finish这个指针出现了问题:然后我更改了一个版本《解决》!

 

/* 扩容 */
void Reserve(size_t n)
{
    if (n > Capacity())
    {
        // 更改:需要记录下_finish的位置,更新完容量后_finish也需要更新位置。
        size_t memaryFinishPos = Size();
​
        // No.1 先开辟新的空间.
        T* temp = new T[n];
​
        // 如果是空的容器,不需要进行拷贝,直接使用指针就可以啦
        if (_start != nullptr)
        {
            // No.2 将原有的数据拷贝到新的内存空间中.
            // memcpy(temp, _start, sizeof(T) * memaryFinishPos);
            // 更改为深拷贝!
            for (size_t i = 0; i < memaryFinishPos; ++i)
            {
                temp[i] = _start[i];
            }
​
            // No.3 销毁掉之前的空间.
            delete[] _start;
        }
        // No.4 使用新的空间
        _start = temp;
        _finish = _start + memaryFinishPos;
        _endofstorage = _start + n;
    }
    
}

3.2.3 调整数据大小

// 扩容 或 扩充数据 或 删除数据。
// n > endofstorage				扩容+填数据.
// finish < n <= endofstorage	填数据.
// n <= endofstorage			删数据.
void Resize(size_t n ,const T val = 0)
{
	// 扩容的大小比原来的容量大->扩容
	if (n > Capacity())
	{
		// 扩容
		Reserve(n);
	}

	// 这里是需要填充数据的,但是不要担心容量不足,因为上面已经会进行判断扩容啦
	// 这里如果没有执行上面的if语句,说明n肯定是小于容量的。
	if (n > Size())
	{
		while (_finish < _start + n)
		{
			*_finish = val;
			++_finish;
		}
	}
	else
	{
		_finish = _start + n;
	}
}

No.3 实现测试三:

3.3.1 打印函数的实现

打印方式我写了三种:是怎么实现的呢?

这里看一下迭代器和operator[]取位置数据<就可以明白了>

// No.1 打印方式一
for (size_t i = 0; i < iVector.Size(); ++i)
{
	cout << iVector[i] << " ";
}
cout << endl;

// No.2 打印方式二
for (auto v : iVector)
{
	cout << v << " ";
}
cout << endl;

// No.3 打印方式三
Vector<int>::iterator itBegin = iVector.begin();
while (itBegin < iVector.end())
{
	cout << *itBegin << " ";
	++itBegin;
}
cout << endl;

3.3.2 迭代器

/* 迭代器:返回容器第一个位置的指针 */
iterator begin()
{
	return _start;
}
const_iterator begin() const
{
	return _start;
}

/* 迭代器:返回容器最后一个位置的指针 */
iterator end()
{
	return _start + Size();
}
const_iterator end() const
{
	return _start + Size();
}

 

3.3.3 operator[]取位置数据

这个代码就先不解释了,返回pos位置的别名,别名的使用一定注意:使用别名要确保空间还是存在的,否则就会和野指针一样!当然野引用和野指针是两个概念,但是意思是一致的。

/* 返回指定位置的数据 */
T& operator[](size_t pos)
{
	return _start[pos];
}
const T& operator[](size_t pos) const
{
	return _start[pos];
}

No.4 实现测试四:

3.4.1 指定位置插入数据函数实现

这个程序是错的,下面图解后才是对的!

/* 在pos位置插入 */
// iterator insert (iterator position, const value_type& val);
iterator Insert(iterator pos, const T& val)
{
	// 检查pos的位置是否正确
	assert(pos >= _start);
	assert(pos <= _finish);

	// 检查是否需要扩容
	if (_finish == _endofstorage)
	{
		// 扩容:我们不知道要扩容多少,这里我们写一个判断:
		size_t newCapacity = Capacity() == 0 ? 4 : Capacity() * 2;
		Reserve(newCapacity);
	}

	// 挪动数据。
	iterator end = _finish - 1;
	while (end >= pos)
	{
		*(end + 1) = *end;
		--end;
	}

	// 数据挪动完了,后将这个位置插入数据。
	*(end + 1) = val;
	++_finish;

	return pos;  // 返回pos位置的数据,
}

 

/* 在pos位置插入 */
// iterator insert (iterator position, const value_type& val);
iterator Insert(iterator pos, const T& val)
{
	// 检查pos的位置是否正确
	assert(pos >= _start);
	assert(pos <= _finish);

	// 检查是否需要扩容
	if (_finish == _endofstorage)
	{
		// 更改:我们需要记录pos原本在容器上是哪一个位置,然后在让他指向新开辟的容器上。
		size_t memaryPos = pos - _start;

		// 扩容:我们不知道要扩容多少,这里我们写一个判断:
		size_t newCapacity = Capacity() == 0 ? 4 : Capacity() * 2;
		Reserve(newCapacity);

		// 恢复pos原本指向的位置!
		pos = _start + memaryPos;
	}

	// 挪动数据。
	iterator end = _finish - 1;
	while (end >= pos)
	{
		*(end + 1) = *end;
		--end;
	}

	// 数据挪动完了,后将这个位置插入数据。
	*(end + 1) = val;
	++_finish;

	return pos;  // 返回pos位置的数据,
}

3.4.2 指定位置删除数据函数实现

/* 在pos位置删除 */
// iterator erase (iterator position);
iterator Erase(iterator pos)
{
	// 检查pos的位置是否正确
	assert(pos >= _start);
	assert(pos < _endofstorage);

	if (!Empty())
	{
		// 迭代
		iterator itBegin = pos;
		while(itBegin < _finish)
		{
			*itBegin = *(itBegin + 1);
			++itBegin;
		}
	}

	--_finish;
	return pos;
}

 

No.5 赋值函数的实现:

3.5.1 operator=赋值实现

这里的代码是错的!正确的代码在图解下面

/* 赋值 */
Vector<T> operator=(Vector<T> v)					//函数正确
{					// 这里需要更改:所以不能用const
	Swap(v);
	return *this;
}
	
// 调用
Vector<int> iVector1;
iVector1.PushBack(1);
iVector1.PushBack(2);
iVector1.PushBack(3);

Vector<int> iVector2 = iVector1;		// 代码没有问题为啥执行错误呢?

 

3.5.2 拷贝构造实现

/* 拷贝构造 */
Vector(const Vector<T>& v)
	:_start(nullptr)
	, _finish(nullptr)
	, _endofstorage(nullptr)
{
	// 开辟一块新的空间,将数据拷贝到新的空间中,函数体内为this
	_start = new T[v.Capacity()];
	memcpy(_start, v._start, sizeof(T) * v.Size());

	_finish = _start + v.Size();
	_endofstorage = _start + v.Capacity();
}

/* 拷贝构造现代写法 */
Vector(const Vector<T>& v)
	:_start(nullptr)
	, _finish(nullptr)
	, _endofstorage(nullptr)
{
	// 利用构造函数构造一个函数
	Vector<T> cv(v.begin(), v.end());
	Swap(cv);   // cv是局部的变量 ,出函数就会调用自己的析构,所以不需要做处理。
}

No.6 其他函数实现:

3.6.1 判断容器是否为空

/* 判断容器是否为空 */
bool Empty()
{
	return _start == _finish;
}

/* 清空容器 */
void Clear()
{
	_finish = _start;
}

/* 交换容器 */
void Swap(Vector<T>& v)
{
	std::swap(_start, v._start);
	std::swap(_finish, v._finish);
	std::swap(_endofstorage, v._endofstorage);
}

练习题验证

题目链接:118. 杨辉三角 - 力扣(Leetcode)

 

class Solution {
public:
    vector<vector<int>> generate(int numRows) 
    {
        // 定义二维的vector :就等于是二维数组。
        vector<vector<int>> vv;
        vv.resize(numRows);    // 开辟外围vector<>的容量空间。

        // 开辟内层vector<vector<int>>的容量。
        for(size_t i = 0; i<vv.size(); ++i)
        {
            // 初始化每一个vector<int>的数据,第一个和最后一个初始化为1.
            vv[i].resize(i + 1, 0);
            vv[i][0] = vv[i][vv[i].size()-1] = 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;
    }
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/77948.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

最近很火的电视剧《点燃我,温暖你》男主角学神和女主角课代表计算机考试实现的跳动的爱心,全程用代码复原真实的实现

最近很火的电视剧《点燃我&#xff0c;温暖你》男主角学神和女主角课代表计算机考试实现的跳动的爱心&#xff0c;全程用代码复原真实的实现。 学神考试100分&#xff0c;只是因为试卷只有100分&#xff01; 该剧改编自作家Twentine创作的小说《打火机与公主裙》&#xff0c;讲…

微服务框架 SpringCloud微服务架构 微服务保护 31 限流规则 31.5 流控效果【排队等待】

微服务框架 【SpringCloudRabbitMQDockerRedis搜索分布式&#xff0c;系统详解springcloud微服务技术栈课程|黑马程序员Java微服务】 微服务保护 文章目录微服务框架微服务保护31 限流规则31.5 流控效果【排队等待】31.5.1 流控效果【排队等待】31.5.2 案例31.5.3 总结31 限流…

JAVA物业管理系统带小程序源码

源码分享&#xff01;文末卡片查看联系方式获取源码。 基于Web&#xff0c;使用MySQL数据库&#xff0c;使用安全框架&#xff1a;shiro, 使用技术&#xff1a;springspringMVCMybatis&#xff0c;小程序 前端框架&#xff1a;layui 编译器&#xff1a;IntelliJ IDEA 项目构…

Linux消息中间件-RabbitMQ

Linux消息中间件-RabbitMQ 消息中间件 MQ简介 MQ 全称为Message Queue, 消息队列。是一种应用程序对应用程序的通信方法。应用程序通过读写出入队列的消息&#xff08;针对应用程序的数据&#xff09;来通信&#xff0c;而无需专用连接来链接它们。消息传递指的是程序之间通…

Python字符串关键点分析介绍

Python字符串关键点有下面几点&#xff1a; 1.一些引号分隔的字符 你可以把字符串看出是Python的一种数据类型&#xff0c;在Python单引号或者双引号之间的字符数组或者连续的字符集合。在python中最常用的引号为&#xff08;’’&#xff09;和("")。两者的功能是一…

笔试强训(四十一)

目录一、选择题二、编程题2.1 Emacs计算器2.1.1 题目2.1.1 题解一、选择题 &#xff08;1&#xff09;某主机的IP地址为180.80.77.55&#xff0c;子网掩码为255.255.252.0.若该主机向其所在子网发送广播分组&#xff0c;则目的地址可以是&#xff08;D&#xff09; A.180.80.7…

【mmdetection系列】mmdetection之训练框架讲解

这一篇是一个比较宽泛的训练框架讲解与说明。会大致说一些模型训练过程中需要的一些模块&#xff0c;datasets、backbone、neck、head、loss等。会先将框架结构&#xff0c;再讲核心机制。 目录 1.框架结构 1.1 configs 1.1.1 训练配置 1.1.2 datasets 1.1.3 models 1.1.…

【Python】基本语法 4 (列表和元组)

列表和元组1. 列表是什么&#xff0c;元组是什么2. 创建列表3. 访问下标4. 切片操作5. 遍历列表元素6. 新增元素7. 查找元素8. 删除元素9. 连接元素10. 关于元组1. 列表是什么&#xff0c;元组是什么 在编程中&#xff0c;变量就是内存空间&#xff0c;用来表示/存储数据。 如果…

java之注解的定义和使用

初识注解&#xff1a; 注解的概念&#xff1a; 注解是从JDK5.0开始引入的新技术&#xff0c;它不是程序本身&#xff0c;但是和注释有相同的作用&#xff0c;都能够对程序做出一定的解释&#xff0c;并且注解能够被其他编译器所读取 注解的格式&#xff1a; 注解是以“注释…

代码随想录训练营第43天|LeetCode 1049. 最后一块石头的重量 II、494. 目标和、474.一和零

参考 代码随想录 题目一&#xff1a;LeetCode 1049. 最后一块石头的重量 II 这个题和之前的“416 分割等和子集”非常的类似&#xff0c;如果给出的stones[]数组能被分成相等的两个数组&#xff0c;那么剩余石头的最小重量就是0。求解思路和分割等和子集一样&#xff0c;只是…

前端工程化实践——javaScript 手写rollup

webpack打包非常繁琐&#xff0c;打包体积较大。rollup主要打包js库。vue/react/angular都在用rollup作为打包工具。 rollup项目初体验 新增文件夹rollupTest 初始化项目&#xff1a;npm init -y 安装依赖 npm install rollup -D 修改配置文件package.json {"name&q…

Python模块fileinput操作文件和目录操作总结

前言 之前介绍Python的 pathlib 模块可以有效的路径及文件查找等方便操作&#xff0c;本篇介绍一个相对 readlines() 获取文件内容更高效的用法 fileinput模块 对一个或者多个文件的内容迭代遍历&#xff08;类似文件操作的readlines()&#xff09;,但是返回的是迭代对象&…

ffmpeg库编译安装及入门指南(Windows篇)- 2022年底钜献

最近项目需要&#xff0c;使用了 ffmpeg 做摄像头视频采集和串流。这几天有点时间&#xff0c;打算把相关的一些知识记录分享一下。 在撰写本文时&#xff0c;我又在另外一台电脑上把 ffmpeg 重新安装了一遍&#xff0c;所以绝对真实靠谱&#xff01;如果你觉得文章写得还不错…

MAC QT OpenGL 波浪特效

目录 一.MAC QT OpenGL 波浪特效效果演示 1.原始图片2.效果演示 二.MAC QT OpenGL 波浪特效源码下载三.其他平台波浪特效版本 1.IOS 演示效果2.Windows OpenGL ES 演示效果3.Windows OpenGL 演示效果 四.猜你喜欢 零基础 OpenGL ES 学习路线推荐 : OpenGL ES 学习目录 >>…

安装微信开发者工具及创建小程序

大纲&#xff1a; 一、官网&#xff1a;微信公众平台微信公众平台&#xff0c;给个人、企业和组织提供业务服务与用户管理能力的全新服务平台。https://mp.weixin.qq.com/ 开发者工具下载页面https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html《开发者…

【教学类-22-02】20221210《八款字体的描字帖-4*4格整张-不用订书机》(大班主题《我是中国人-中国字》)

成品样式&#xff1a; 48号字&#xff08;适应2-3个名字的大小&#xff09; 44号字&#xff08;适应4个名字大小&#xff08;2-3个名字也可以用&#xff0c;字会稍微小&#xff09;&#xff09; 打印样式&#xff1a; 背景需求&#xff1a; 观摩中3班做“描花体字”的本子的情…

【前沿技术RPA】 一文了解 UiPath 状态机 State Machine

&#x1f40b;作者简介&#xff1a;博主是一位.Net开发者&#xff0c;同时也是RPA和低代码平台的践行者。 &#x1f42c;个人主页&#xff1a;会敲键盘的肘子 &#x1f430;系列专栏&#xff1a;UiPath &#x1f980;专栏简介&#xff1a;UiPath在传统的RPA&#xff08;Robotic…

【流量回放探索】啄木鸟流量回放平台

啄木鸟流量回放平台 项目简介 本项目是基于RuoYi-Vue单应用版本流量回放引擎基于goreplay演示系统基于newbee-mall-api-go 如果你想从录制开始体验&#xff0c;需要搭建演示系统newbee-mall-api-go &#xff0c;同时需要将gor 部署在演示系统服务中&#xff0c;搭建kafka以便…

[附源码]Python计算机毕业设计SSM基于技术的高校学生勤工俭学管理系统的设计与开发(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

HBase

1 HBase存储结构 HMaster 1. 监控 RegionServer   2. 处理 RegionServer 故障转移   3. 处理元数据的变更   4. 处理 region 的分配或移除   5. 在空闲时间进行数据的负载均衡   6. 通过 Zookeeper 发布自己的位置给客户端 RegionServer 1. 负责存储 HBase 的实际数…