【C++】简化源码——vector的模拟实现

news2025/11/5 1:14:17

文章目录

    • 一、前言
    • 二、无参构造&析构
    • 三、基础接口
      • 1.empty和clear
      • 2.size和capacity
      • 3.[]和iterator
    • 四、resize和reserve
    • 五、尾插尾删
    • 六、其他构造
    • 七、迭代器失效
      • 1.insert
      • 2.erase
    • 八、memcpy问题
    • 九、vector.h

一、前言

本篇的目的很简单,只有一个:模拟实现vector

如何去模拟实现?我们可以看看vector的源码,我们可以抽离出主体框架:

template <class T, class Alloc = alloc>
class vector {
     typedef T value_type;
     typedef value_type* iterator;
     typedef const value_type* const_iterator;
protected:
    iterator start;
    iterator finish;
    iterator end_of_storage;
}

这本质上与T*a,size_t size,size_t capacity是类似的:

image-20221125100737422

对于size = _finish - _start

对于capacity = _endofstorage-_start

有了这些作为铺垫,我们对于vector的模拟实现大概有了一个基本的框架,话不多说,直接进入主题👇


二、无参构造&析构

对于这两个都是我们熟悉的老朋友了

  • 无参构造

初始化全设置为空:

vector()
    :_start(nullptr)
    , _finish(nullptr)
    , _endofstorage(nullptr)
{}
  • 析构
~vector()
{
    delete[] _start;
    _start = _finish = _endofstorage = nullptr;
}

三、基础接口

1.empty和clear

empty

bool empty() const
{
    return _finish == _start;
}

clear

void clear()
{
    _finish = _start;//这里可不是置为nullptr哦
}

2.size和capacity

size

size_t size() const
{
    return _finish - _start;
}

capacity

size_t capacity() const 
{
    return _endofstorage - _start;
}

3.[]和iterator

[]

提供const版本和非const版本:

T& operator[](size_t pos)
{
    assert(pos < size());
    return _start[pos];
}

const T& operator[](size_t pos)const
{
    assert(pos < size());
	return _start[pos];
}

iterator

同理普通迭代器和const迭代器版本,同理,范围for循环此时也是可以实现的:

typedef T* iterator;
typedef const T* const_iterator;
		iterator begin()
		{
			return _start;
		}
		iterator end()
		{
			return _finish;
		}
		const_iterator begin() const
		{
			return _start;
		}
		const_iterator end() const
		{
			return _finish;
		}

四、resize和reserve

这两个接口需要单独拎出来,这是因为后面的插入等相关操作需要用到,所以我们先来看看这两个接口,同时这里有一些问题值得我们去注意:

resize

n个数据去初始化,这个n是多大,会造成什么影响?我们需要进行分类讨论:

image-20221126223553778

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

resize的参数初始化值为T类型的构造,这里可不能直接初始化为0,要是T是自定义类型呢?是vector呢?所以这里如果T是vector的化调用的就是vector的构造函数。另外,这里还需要注意的一点是:构造vector的时候是匿名对象,匿名对象具有常性,不可修改所以要加上const修饰

所以,我们自然而然可以知道,对于内置类型比如int,都是有构造函数的:

image-20221126225324427

reserve

reserve最大的问题就是深拷贝!开辟新空间进行赋值的时候如果直接使用memcpy是浅拷贝

void reserve(size_t n)
{
    if (n > capacity())
    {
        T* tmp = new T[n];
        //size()需要先保存起来,后面_start会发生改变
		size_t sz = size();
        //为空不需要拷贝了
        if (_start)
        {
            for (size_t i = 0; i < sz; i++)
            {
                tmp[i] = _start[i];
            }
            delete[] _start;
            //memcpy(tmp, _start, sizeof(T) * size());//浅拷贝
            //delete[] _start;
        }
        _start = tmp;
        _finish = _start+sz;
        _endofstorage = _start + n;
    }
}
  • size()需要先用sz保存

为啥?如果没有保存:

_start = tmp;
_finish = _start+size();
_endofstorge = _start+n;

不要忘了:size()=_finish-_start, 而_start = tmp会更新_start;根本就不是原来的size()了

  • 使用memcpy问题

memcpy拷贝数据,拿vector<vector>作为例子,其中vector仍然是浅拷贝的,对于自定义类型出现问题

vector<vector<int>> vv;
vector<int> v(4, 1);
//复用push_back尾插
vv.push_back(v);
vv.push_back(v);
vv.push_back(v);
vv.push_back(v);
//需要扩容成2倍
vv.push_back(v);
for (size_t i = 0; i < vv.size(); i++)
{
for (size_t j = 0; j < vv[i].size(); j++)
{
  cout << vv[i][j] << " ";
}
cout << endl;
}

image-20221127001111216


五、尾插尾删

void push_back(const T& x)
{
    if (_finish == _endofstorage)
    {
        size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;
        reserve(newCapacity);
    }
    *_finish = x;
    ++_finish;
}


void pop_back()
{
    assert(_finish > _start);
    --_finish;
}

六、其他构造

这里需要复用前面的一些接口,所以放在这个地方

  • 迭代器区间构造

这里复用了push_back,而且写成了模板

template <class InputIterator>
    //模板可以使用其他迭代器区间
vector(InputIterator first, InputIterator last)
    :_start(nullptr)
	, _finish(nullptr)
	, _endofstorage(nullptr)
    {
        while (first != last)
        {
            push_back(*first);//int不能解引用
            ++first;
        }
    }

类模板的成员函数可以是函数模板,使之可以是任意类型的迭代器区间,包括了自身的迭代器区间构造

另外,初始化列表全部初始化为nullptr,没有初始化就是随机值,出现野指针

  • 拷贝构造

初始化列表全都要初始化为nullptr,否则就是随机值

//写法1
vector(const vector<T>& v)
    :_start(nullptr)
	,_finish(nullptr)
	,_endofstorage(nullptr)
    {
        reserve(v.capacity());
        for (const auto& e : v)
        {
            push_back(e);
        }
    }

//写法2
void swap(vector<T>& v)
{
    std::swap(_start, v._start);
    std::swap(_finish, v._finish);
    std::swap(_endofstorage, v._endofstorage);
}

vector(const vector<T>& v)
    :_start(nullptr)
    , _finish(nullptr)
	, _endofstorage(nullptr)
    {
        vector<T> tmp(v.begin(), v.end());
        swap(tmp);
    }
  • 赋值重载

赋值重载需要复用拷贝构造

	//缺陷:自己拷贝自己
//v1(v2)
vector<T>& operator=(vector<T> v)
{
    swap(v);
    return *this;
}

这种写法就是有一个小问题:如果是自己拷贝自己呢?加个判断?没用,因为此时已经传值传参过来了,加个判断没啥意义了。但是这个问题不大,我们允许存在,平时自己也很少自己赋值自己。

另外,这里是传值调用,有人会说了:传引用也可以啊,此时如果是引用的话,v2赋值给v1,v1不是v2的拷贝,直接把v2换成了v1,v1换给了v2,v2本身已经发生变化了,这不是赋值了。


七、迭代器失效

1.insert

insert这个太熟悉了,废话不多说直接上手代码:

//迭代器失效:扩容引起野指针问题
void insert(iterator pos, const T& val)
{
    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;
    }
    *pos = val;
    ++_finish;
}

测试代码:

void Test3()
{
		vector<int> v;
		v.push_back(1);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);

		for (size_t i = 0; i < v.size(); i++)
		{
			cout << v[i] << " ";
		}
		cout << endl;
		vector<int>::iterator it = find(v.begin(), v.end(), 3);
		if (it != v.end())
		{
			v.insert(it, 30);
		}
		for (auto e : v)
		{
			cout << e << " ";
		}
		cout << endl;
}

image-20221127102831809

这是因为扩容导致pos失效了:

image-20221127103607613

insert过程中发生扩容,导致it指向的空间实际上已经被释放,it指向已被释放的空间是野指针,造成了迭代器失效

所以,我们应该去更新pos,算出pos刚开始的相对位置,然后再去进行更新即可解决问题。但是此时外面调用insert的it仍然是失效的,因为是传值调用,形参改变不影响实参,可以通过返回值接收解决问题。(如果是传引用的话,只能传变量,而临时对象具有常性,不能调用,存在很多问题),所以直接用返回值解决。

iterator insert(iterator pos, const T& val)
{
    assert(pos >= _start);
	assert(pos <= _finish);
    if (_finish == _endofstorage)
    {
        //扩容会导致pos迭代器失效,需要更新
        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 = val;
    ++_finish;
    return pos;
}

测试代码:

void Test3()
{
    vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	for (size_t i = 0; i < v.size(); i++)
    {
        cout << v[i] << " ";
    }
    cout << endl;
    v.insert(v.begin(), 0);
    vector<int>::iterator it = find(v.begin(), v.end(), 3);
    if (it != v.end())
    {
        v.insert(it, 30);
    }
    for (auto e : v)
    {
        cout << e << " ";
    }
    cout << endl;
}

image-20221127110313361

2.erase

挪动数据进行覆盖即可

void erase(iterator pos)
{
    assert(pos >= _start);
    assert(pos < _finish);
    iterator begin = pos + 1;
    while (begin < _finish)
    {
        *(begin - 1) = *begin;
        ++begin;
    }
    --_finish;
}

erase的pos也可能会导致pos失效,测试代码:

void Test6()
	{
		//删除所有偶数
		vector<int> v;
		v.push_back(1);
		v.push_back(2);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);
		vector<int>::iterator it = v.begin();
		while (it != v.end())
		{
			if (*it % 2 == 0)
			{
				v.erase(it);
			}
			++it;
		}
		for (auto e : v)
		{
			cout << e << " ";
		}
		cout << endl;
	}

测试发现:

1,2,3,4的时候发生崩溃

1,2,2,3,5结果只删了一个2

1,2,3,4,5结果是正常的

image-20221127140628122

上述代码在VS下,当erase(it)之后,it指向的位置发生改变,然后在++it的话,会出现问题,出现一些错误,造成迭代器失效。

我们最好统一认为失效了。

正确的erase:

iterator erase(iterator pos)
{
    assert(pos >= _start);
    assert(pos < _finish);
    iterator begin = pos + 1;
    while (begin < _finish)
    {
        *(begin - 1) = *begin;
        ++begin;
    }
    --_finish;
    return pos;
}		

测试代码:

void Test6()
	{
		//删除所有偶数
		vector<int> v;
		v.push_back(1);
		v.push_back(2);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);
		vector<int>::iterator it = v.begin();
		while (it != v.end())
		{
			if (*it % 2 == 0)
			{
				it = v.erase(it);
			}
			else
			{
				++it;
			}
		}
		for (auto e : v)
		{
			cout << e << " ";
		}
		cout << endl;
	}

image-20221127121509374

对于insert和erase存在迭代器失效的问题,当迭代器失效而来,我们就不要再去访问pos的位置了,要更新pos的位置,可通过返回值接收进行访问,


八、memcpy问题

如果拷贝的是内置类型的元素,memcpy即高效又不会出错,但如果拷贝的是自定义类型元素,并且自定义类型元素中涉及到资源管理时,就会出错,因为memcpy的拷贝实际是浅拷贝,指向同一块空间,假设我们仍然在reserve接口中使用memcpy进行拷贝:

我们以vector类为例子:

void Test10()
{
    vector<string> v;
    v.push_back("11111111111111111111");
    v.push_back("22222222222222222222");
    v.push_back("33333333333333333333");
    v.push_back("44444444444444444444");
    v.push_back("55555555555555555555");
    for (auto e : v)
    {
        cout << e << " ";
    }
    cout << endl;
}

image-20221127143935504

image-20221127145226373

所以我们要调用自己的拷贝,一个一个进行深拷贝。


九、vector.h

#define _CRT_SECURE_NO_WARNINGS

#pragma once

namespace hwc
{
	template<class T>
	class vector
	{
	public:
		typedef T* iterator;
		typedef const T* const_iterator;

		iterator begin()
		{
			return _start;
		}
		iterator end()
		{
			return _finish;
		}
		const_iterator begin() const
		{
			return _start;
		}
		const_iterator end() const
		{
			return _finish;
		}

		T& operator[](size_t pos)
		{
			assert(pos < size());
			return _start[pos];
		}

		const T& operator[](size_t pos)const
		{
			assert(pos < size());
			return _start[pos];
		}

		vector()
			:_start(nullptr)
			, _finish(nullptr)
			, _endofstorage(nullptr)
		{}

		/*vector(const vector<T>& v)
			:_start(nullptr)
			,_finish(nullptr)
			,_endofstorage(nullptr)
		{
			reserve(v.capacity());
			for (const auto& e : v)
			{
				push_back(e);
			}
		}*/


		//vector<int> v1(10, 5);
		//vector<char> v2(10, 'A');
		vector(size_t n, const T& val = T())
			:_start(nullptr)
			, _finish(nullptr)
			, _endofstorage(nullptr)
		{
			reserve(n);
			for (size_t i = 0; i < n; i++)
			{
				push_back(val);
			}
		}
		//改成int或强转
		vector(int n, const T& val = T())
			:_start(nullptr)
			, _finish(nullptr)
			, _endofstorage(nullptr)
		{
			reserve(n);
			for (int i = 0; i < n; i++)
			{
				push_back(val);
			}
		}

		template <class InputIterator>
		vector(InputIterator first, InputIterator last)
			:_start(nullptr)
			, _finish(nullptr)
			, _endofstorage(nullptr)
		{
			while (first != last)
			{
				push_back(*first);//int不能解引用
				++first;
			}
		}
		vector(const vector<T>& v)
			:_start(nullptr)
			, _finish(nullptr)
			, _endofstorage(nullptr)
		{
			vector<T> tmp(v.begin(), v.end());
			swap(tmp);
		}

		//缺陷:自己拷贝自己
		vector<T>& operator=(vector<T> v)
		{

			swap(v);
			return *this;
		}
		~vector()
		{
			delete[] _start;
			_start = _finish = _endofstorage = nullptr;
		}

		void reserve(size_t n)
		{
			if (n > capacity())
			{
				T* tmp = new T[n];
				size_t sz = size();
				//为空不需要拷贝了
				if (_start)
				{
					for (size_t i = 0; i < sz; i++)
					{
						tmp[i] = _start[i];
					}
					delete[] _start;
					//memcpy(tmp, _start, sizeof(T) * size());//浅拷贝
					//delete[] _start;
				}
				_start = tmp;
				_finish = _start + sz;
				_endofstorage = _start + n;
			}
		}

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

		bool empty() const
		{
			return _finish == _start;
		}

		size_t size() const
		{
			return _finish - _start;
		}
		size_t capacity() const
		{
			return _endofstorage - _start;
		}
		void push_back(const T& x)
		{
			if (_finish == _endofstorage)
			{
				size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;
				reserve(newCapacity);
			}

			*_finish = x;
			++_finish;
		}

		void pop_back()
		{
			assert(_finish > _start);
			--_finish;
		}

		//迭代器失效:野指针问题
		/*void insert(iterator pos, const T& val)
		{
			assert(pos >= _start);
			assert(pos < _finish);
			if (_finish == _endofstorge)
			{
				reserve(capacity() == 0 ? 4 : capacity() * 2);
			}
			iterator end = _finish - 1;
			while (end >= pos)
			{
				*(end + 1) = *end;
				--end;
			}
			*pos = val;
			++_finish;
		}*/

		iterator insert(iterator pos, const T& val)
		{
			assert(pos >= _start);
			assert(pos <= _finish);
			if (_finish == _endofstorage)
			{
				//扩容会导致pos迭代器失效,需要更新
				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 = val;
			++_finish;

			return pos;
		}

		iterator erase(iterator pos)
		{
			assert(pos >= _start);
			assert(pos < _finish);
			iterator begin = pos + 1;
			while (begin < _finish)
			{
				*(begin - 1) = *begin;
				++begin;
			}
			--_finish;
			return pos;
		}

		void swap(vector<T>& v)
		{
			std::swap(_start, v._start);
			std::swap(_finish, v._finish);
			std::swap(_endofstorage, v._endofstorage);
		}

		void clear()
		{
			_finish = _start;
		}
	public:
		iterator _start;
		iterator _finish;
		iterator _endofstorage;
	};


	void Test1()
	{
		vector<int> v;
		v.push_back(1);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);
		v.push_back(5);

		cout << v.size() << endl;
		cout << v.capacity() << endl;

		for (size_t i = 0; i < v.size(); i++)
		{
			cout << v[i] << " ";
		}
		cout << endl;

		vector<int>::iterator it = v.begin();
		while (it != v.end())
		{
			cout << *it << " ";
			++it;
		}
		cout << endl;
		for (auto e : v)
		{
			cout << e << " ";
		}
		cout << endl;
	}

	void Test2()
	{
		vector<int> v;
		v.resize(10, -1);
		for (auto e : v)
		{
			cout << e << " ";
		}
		cout << endl;

		v.resize(5);
		for (auto e : v)
		{
			cout << e << " ";
		}
		cout << endl;
		v.pop_back();
		v.pop_back();
		v.pop_back();
		v.pop_back();
		v.pop_back();
		v.pop_back();
	}
	void Test3()
	{
		vector<int> v;
		v.push_back(1);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);

		for (size_t i = 0; i < v.size(); i++)
		{
			cout << v[i] << " ";
		}
		cout << endl;
		v.insert(v.end(), 0);
		for (auto e : v)
		{
			cout << e << " ";
		}
		cout << endl;

		vector<int>::iterator it = find(v.begin(), v.end(), 3);
		if (it != v.end())
		{
			v.insert(it, 30);
		}
		for (auto e : v)
		{
			cout << e << " ";
		}
		cout << endl;
	}

	void Test4()
	{
		vector<int> v;
		v.push_back(1);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);
		vector<int>::iterator it = find(v.begin(), v.end(), 3);
		if (it != v.end())
		{
			//传值
			v.insert(it, 30);
		}
		//insert以后it不能使用,可能迭代器失效(野指针)
		//(*it)++;

		for (auto e : v)
		{
			cout << e << " ";
		}
		cout << endl;
	}
	void Test5()
	{
		vector<int> v;
		v.push_back(1);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);
		vector<int>::iterator it = find(v.begin(), v.end(), 3);
		if (it != v.end())
		{
			v.erase(it);
		}
		cout << *it << endl;
		//	(*it)++;
		for (auto e : v)
		{
			cout << e << " ";
		}
		cout << endl;
	}


	void Test6()
	{
		//删除所有偶数
		vector<int> v;
		v.push_back(1);
		v.push_back(2);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);
		v.push_back(5);

		vector<int>::iterator it = v.begin();
		while (it != v.end())
		{
			if (*it % 2 == 0)
			{
				it = v.erase(it);
			}
			else
			{
				++it;
			}
		}
		for (auto e : v)
		{
			cout << e << " ";
		}
		cout << endl;
	}

	void Test7()
	{
		vector<int> v;
		v.push_back(1);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);
		v.push_back(5);
		vector<int> v1(v);
		for (auto e : v1)
		{
			cout << e << " ";
		}
		cout << endl;

		vector<int> v2;
		v2.push_back(10);
		v2.push_back(20);
		v2.push_back(30);
		v1 = v2;
		for (auto e : v1)
		{
			cout << e << " ";
		}
		cout << endl;

		v1 = v1;
		for (auto e : v1)
		{
			cout << e << " ";
		}
		cout << endl;
	}


	void Test8()
	{
		string str("hello world");

		vector<int> v(str.begin(), str.end());
		for (auto e : v)
		{
			cout << e << " ";
		}
		cout << endl;

		/*vector<int> v1(v.begin(), v.end());*/
		vector<int> v1(10, 5);
		//vector<char> v2(10, 'A');
		for (auto e : v1)
		{
			cout << e << " ";
		}
		cout << endl;
	}



	class Solution {
	public:
		vector<vector<int>> generate(int numRows) {
			vector<vector<int>> vv;
			vv.resize(numRows);
			for (size_t i = 0; i < vv.size(); i++)
			{
				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;
		}
	};

	void Test9()
	{
		vector<vector<int>> vvRet = Solution().generate(5);

		for (size_t i = 0; i < vvRet.size(); i++)
		{
			for (size_t j = 0; j < vvRet[i].size(); j++)
			{
				cout << vvRet[i][j] << " ";
			}
			cout << endl;
		}
		/*vector<vector<int>> vv;
		vector<int> v(10, 1);
		vv.push_back(v);
		vv.push_back(v);
		vv.push_back(v);
		vv.push_back(v);
		vv.push_back(v);

		for (size_t i = 0; i < vv.size(); i++)
		{
			for (size_t j = 0; j < vv[i].size(); j++)
			{
				cout << vv[i][j] << " ";
			}
			cout << endl;
		}*/
		cout << endl;
	}

	void Test10()
	{
		vector<string> v;
		v.push_back("11111111111111111111");
		v.push_back("22222222222222222222");
		v.push_back("33333333333333333333");
		v.push_back("44444444444444444444");
		v.push_back("55555555555555555555");

		for (auto e : v)
		{
			cout << e << " ";
		}
		cout << endl;
	}
}

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

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

相关文章

C语言刷题(一)

&#x1f412;博客名&#xff1a;平凡的小苏 &#x1f4da;学习格言&#xff1a;别人可以拷贝我的模式&#xff0c;但不能拷贝我不断往前的激情 目录 用递归法求一个整数一维数组a的最大元素 猴子吃桃问题 奇偶数换位问题 水仙花数&#xff08;0-100000&#xff09; 换啤酒…

web前端电影项目作业源码 大学生影视主题网页制作电影网页设计模板 学生静态网页作业成品 dreamweaver电影HTML网站制作

HTML实例网页代码, 本实例适合于初学HTML的同学。该实例里面有设置了css的样式设置&#xff0c;有div的样式格局&#xff0c;这个实例比较全面&#xff0c;有助于同学的学习,本文将介绍如何通过从头开始设计个人网站并将其转换为代码的过程来实践设计。 文章目录一、网页介绍一…

redis命令行操作库、键、和五大数据类型详解

一、数据库操作命令 redis默认有16个数据库&#xff0c;类似数组下标从0开始&#xff0c;初始默认使用0号库。 1.1 测试是否连通 ping测试服务器是否连通 返回pone就是连通了 1.2 切换数据库 select index1.3 数据移动 move key db1.4 显示数据总量 dbsize1.5 数据清除 …

Maven 跳过测试的几种方式

在 Maven 对项目进行编译的时候&#xff0c;我们通常可能会希望跳过复杂的测试。 尤其是在开始项目还不是非常稳定的阶段。 命令行中使用 -Dmaven.test.skiptrue 在命令行&#xff0c;只要简单的给任何目标添加 maven.test.skip 属性就能跳过测试&#xff1a; mvn install …

leetcode:6251. 统计回文子序列数目【dp + 统计xy子序列出现的个数】

目录题目截图题目分析ac code总结题目截图 题目分析 固定了中间的数i后从两边选xy 和 yx对于x y的情况&#xff0c;比较简单预处理每个数字出现的index为ids然后看看两边x各自的个数n1 n2n1和n2必须大于等于2左边可以选n1 * (n1 - 1) // 2右边可以选n2 * (n2 - 1) // 2两边乘…

【C++】通过哈希表实现map和set

前言 在前面&#xff0c;我们通过红黑树这一底层结构实现了map和set。它们是关联式容器。而现在&#xff0c;我们将通过哈希表这一数据结构重新实现map和set&#xff0c;即unordered系列的关联式容器。因为它们的遍历是无序的&#xff0c;和平衡二叉树不同&#xff0c;不能做到…

APOLLO UDACITY自动驾驶课程笔记——规划、控制

1、路径规划使用三个输入&#xff0c;第一个输入为地图&#xff0c;Apollo提供的地图数据包括公路网和实时交通信息。第二个输入为我们当前在地图上的位置。第三个输入为我们的目的地&#xff0c;目的地取决于车辆中的乘客。 2、将地图转为图形 该图形由“节点”(node)和“边缘…

直流潮流计算matlab程序

一、直流潮流计算原理 直流潮流发的特点是用电力系统的交流潮流&#xff08;有功功率和无功功率&#xff09;等值的直流电流来代替。甚至只用直流电路的解析法来分析电力系统的有功潮流&#xff0c;而不考虑无功分布对有功的影响。这样一来计算速度加快&#xff0c;但计算的准确…

Rocket MQ : 拒绝神化零拷贝

注: 本文绝非对零拷贝机制的否定笔者能力有限&#xff0c;理解偏差请大家多多指正不可否认零拷贝对于Rocket MQ的高性能表现有着积极正面的作用&#xff0c;但是笔者认为只是锦上添花&#xff0c;并非决定性因素。Rocket MQ性能卓越的原因绝非零拷贝就可以一言以蔽之。 笔者企图…

第146篇 笔记-智能合约介绍

定义&#xff1a;当满足某些预定义条件时&#xff0c;智能合约是一种在区块链网络上运行的防篡改程序。 1.什么是智能合约 智能合约是在区块链网络上托管和执行的计算机程序。每个智能合约都包含指定预定条件的代码&#xff0c;这些条件在满足时会触发并产生结果。通过在去中…

IDEA热部署插件JRebel and XRebel

IDEA热部署插件JRebel and XRebel嘚吧嘚下载安装激活配置使用嘚吧嘚 刚开始用过一段时间的eclipse&#xff0c;其他方面没感觉&#xff0c;但是eclipse的热部署真的是深得我心啊&#x1f60a;。 后来换了IDEA&#xff0c;瞬间就心动了&#xff0c;各个方面真的很好用&#xf…

U3D VideoPlayer播放视频和坑点

最近做的游戏里,需要先播放一段几秒钟的工作室LOGO片头,拿到的视频是AVI格式,以前没在U3D里用到过视频,本以为很简单,没想到都2022年了,U3D播放视频还这么烂。。。 插件最好用的是AVPro,除非你有大量的视频要播放,否则没必要用插件,一个是贵,另一个插件很大。 首先…

Python爬虫从入门到进阶

前言 董伟明&#xff0c;国内某知名Python应用网站高级产品开发工程师&#xff0c;《 Python Web 开发实战》作者&#xff0c;本书目前已经售出 17k 余本&#xff0c;另外也已经在台湾地区上市。在 2012 和 2014 年分别通过 2 个爬虫免试获得 2 个业界知名公司 offer&#xff…

MyBatis缓存机制之一级缓存

MyBatis缓存机制之一级缓存 前言 MyBatis内部封装了JDBC&#xff0c;简化了加载驱动、创建连接、创建statement等繁杂的过程&#xff0c;是我们常见的持久性框架。缓存是在计算机内存中保存的临时数据&#xff0c;读取时无需再从磁盘中读取&#xff0c;从而减少数据库的查询次…

Node.js 入门教程 1 Node.js 简介

Node.js 入门教程 Node.js官方入门教程 Node.js中文网 本文仅用于学习记录&#xff0c;不存在任何商业用途&#xff0c;如侵删 文章目录Node.js 入门教程1 Node.js 简介1.1 大量的库1.2 Node.js 应用程序的示例1.3 Node.js框架和工具1 Node.js 简介 Node.js 是一个开源和跨平台…

子矩形计数(冬季每日一题 17)

给定一个长度为 nnn 的数组 aaa 和一个长度为 mmm 的数组 bbb。 两个数组均只包含 000 和 111。 利用两个给定数组生成一个 nmnmnm 的矩阵 ccc&#xff0c;其中 cijaibjc_{ij}a_ib_jcij​ai​bj​。 显然&#xff0c;矩阵 ccc 中也只包含 000 和 111。 请问&#xff0c;矩阵…

期末复习 c

作者&#xff1a;小萌新 专栏&#xff1a;C语言复习 作者简介&#xff1a; 大二学生 希望能和大家一起进步&#xff01; 本篇博客简介&#xff1a;回顾之前的分支循环以及一些题目博客 [TOC](这里写目录标题分支循环选择switch casegetchar putchar 以及EOF三个C语言练习题总结…

C++智能指针之unique_ptr

C智能指针之unique_ptr前言一、unique_ptr1.1 unique_ptr类的初始化1.2 unique_ptr禁止拷贝和赋值1.3 release、reset函数1.4 向unique_ptr传递删除器1.5 unique_ptr与动态数组的使用总结前言 在C中&#xff0c;动态内存的申请和释放是通过运算符&#xff1a;new 和 delete 进行…

【无线传感器】基于Matlab实现WSN 查找两个节点之间的最短路径并发送数据

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;修心和技术同步精进&#xff0c;matlab项目合作可私信。 &#x1f34e;个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知。 更多Matlab仿真内容点击&#x1f447; 智能优化算法 …

Linux基础内容(13)—— 进程控制

目录 1.fork函数的进程创建 1.fork返回值 2.fork返回值 3.fork调用失败 2.写时拷贝 3.退出码的知识 4.进程退出 1.退出的情况 2.正常退出 5.进程等待 1.调用系统等待函数杀死僵尸进程 2.僵尸状态与PCB的关系 3.进程阻塞等待与非阻塞等待方式 6.进程程序替换 1.替…