【C++】list 常见使用和模拟实现

作者主页:lightqjx

本文专栏:C++

目录

一、list简介

二、list的使用

[1. 常见接口](#1. 常见接口)

[2. list的操作](#2. list的操作)

[3. list的迭代器问题](#3. list的迭代器问题)

三、list的模拟实现

[1. 定义成员变量](#1. 定义成员变量)

[2. 迭代器的实现(重点)](#2. 迭代器的实现(重点))

(1)关键部分实现

(2)const迭代器

(3)完善list的迭代器

[3. 插入](#3. 插入)

[4. 删除](#4. 删除)

[5. 构造&析构](#5. 构造&析构)

[6. 求list中的数据个数](#6. 求list中的数据个数)

四、总代码参考


一、list简介

对list的详细介绍,在链接中的解释是比较详细的:list文档介绍

简而言之,lis的结构就是一个带哨兵位的双向循环链表 。它是标准模板库(STL)提供的双向链表容器, 是一种可以在常数范围内在任意位置进行插入和删除的序列式容器。

它的优点是插入/删除高效(O(1)的时间),支持双向遍历。缺陷是不支持随机访问(O(n)的时间)。

list和vector一样,都是通过模板定义的,所以定义时都是需要指明类型的。如图:

使用时要注意需要包含头文件list,如以下代码:

cpp 复制代码
#include <list>
int main()
{
	std::list<int> ls1;
	std::list<char> ls2;
	std::list<double> ls3;
	return 0;
}

二、list的使用

1. 常见接口

在list中的接口有许多,但它也是STL中的一个容器,大多数的接口的使用都比较类似,比如:构造,析构等等,都是非常容易掌握如何正确的使用的。如下图就是第一个list的一些部分接口:

可以发现,它们和string,vector都差不多,提供了迭代器,一些增删查改的操作等等操作。它们的使用其实都是差不多的,这里就不一一介绍了,若有还疑问,可以通过查阅本文章开头的list文档进行更多了解。这里我们来使用list的一些基本操作:

cpp 复制代码
#include <list>
#include<iostream>
using namespace std;
int main()
{
	list<int> lt;
	// 尾插
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);
	lt.push_back(5);
	auto it = lt.begin();
	while (it != lt.end())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;
	// 头插
	lt.push_front(10);
	lt.push_front(20);
	lt.push_front(30);

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

	//注意list的迭代器没有重载+
	//在第3个位置后插入666
	auto it2 = lt.begin();
	for (int i = 0; i < 3; i++)
	{
		++it2;
	}
	lt.insert(it2, 666);
	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;

	lt.erase(lt.begin()); //删除第一个位置
	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;

	//......
	return 0;
}

2. list的操作

与其他容器不同的常见的几个接口,如下所示:

splice 功能: 将元素从一个列表转移到另一个列表或同一列表的另一个位置。

这里我们来示范第一个:

cpp 复制代码
#include <list>
#include<iostream>
using namespace std;
int main()
{
	list<int> lt1;
	lt1.push_back(1);
	lt1.push_back(2);
	lt1.push_back(3);
	lt1.push_back(4);
	lt1.push_back(5);

	list<int> lt2;
	lt2.push_back(10);
	lt2.push_back(20);
	lt2.push_back(30);
	lt2.push_back(40);
	lt2.push_back(50);

	lt1.splice(lt1.begin(), lt2);//将lt2移动到lt1的开头,这是通过结点链接的,直接将结点连接在一起
	for (auto e : lt1)
	{
		cout << e << " ";
	}
	cout << endl;
	return 0;
}

remove 功能: 从列表中移除所有等于特定值的元素,相当于 find+erase,没找到啥也不干。

示例:

cpp 复制代码
#include <list>
#include<iostream>
using namespace std;
int main()
{
	list<int> lt1;
	lt1.push_back(1);
	lt1.push_back(2);
	lt1.push_back(3);
	lt1.push_back(4);
	lt1.push_back(5);
	for (auto e : lt1)
	{
		cout << e << " ";
	}
	cout << endl;

	lt1.remove(3); //移除三这个元素
	for (auto e : lt1)
	{
		cout << e << " ";
	}
	cout << endl;
	return 0;
}

remove_if 涉及仿函数,暂时不说。

unique 功能: 移除连续重复的元素,仅保留一组连续相同 元素中的一个。

示例:

cpp 复制代码
#include <list>
#include<iostream>
using namespace std;
int main()
{
	list<int> lt1;
	int arr[10] = { 1,1,1,3,5,5,6,1,1,3 };
	for (int i = 0; i < 10; i++)
	{
		lt1.push_back(arr[i]);
	}
	for (auto e : lt1)
	{
		cout << e << " ";
	}
	cout << endl;

	lt1.unique();
	for (auto e : lt1)
	{
		cout << e << " ";
	}
	cout << endl; //输出:1 3 5 6 1 3
	return 0;
}

merge 功能: 合并两个已排序 的列表,合并后原列表被清空,合并结果存储在当前列表中。

示例:

cpp 复制代码
#include <list>
#include<iostream>
using namespace std;
int main()
{
	list<int> lt1;
	lt1.push_back(1);
	lt1.push_back(2);
	lt1.push_back(3);
	lt1.push_back(4);
	lt1.push_back(5);

	list<int> lt2;
	lt2.push_back(1);
	lt2.push_back(2);
	lt2.push_back(3);
	lt2.push_back(4);
	lt2.push_back(5);

	lt1.merge(lt2);
	for (auto e : lt1)
	{
		cout << e << " "; //输出:1 1 2 2 3 3 4 4 5 5
	}
	cout << endl;

	for (auto e : lt2)
	{
		cout << e << " ";//原列表lt2会被清空
	}
	cout << endl;
	return 0;
}

sort 功能: 对列表中的元素进行排序。

但要注意这里的list库里面的sort实际上没有什么意义,因为在我算法库里面已经实现了一个sort是用快排实现的,而在这里的底层实现是归并排序,效率比较低,不能频繁使用。这里list库里面的sort的实际意义就是使用起来比较方便。使用list中的sort如下所示:

cpp 复制代码
#include <list>
#include<iostream>
using namespace std;
int main()
{
	list<int> lt1;
	list<int> lt2;
	int arr[10] = { 10,9,8,7,6,5,4,3,2,1 };
	for (int i = 0; i < 10; i++)
	{
		lt1.push_back(arr[i]);
		lt2.push_back(arr[i]);
	}

	//lt1.sort();
	for (auto e : lt1)
	{
		cout << e << " "; //输出:1,2,3,4,5,6,7,8,9,10
	}
	cout << endl;
	return 0;
}

reverse 功能: 反转列表中元素的顺序。

示例:

cpp 复制代码
#include <list>
#include<iostream>
using namespace std;
int main()
{
	list<int> lt1;
	int arr[10] = { 10,5,6,7,6,5,4,3,2,1 };
	for (int i = 0; i < 10; i++)
	{
		lt1.push_back(arr[i]);
	}

	lt1.reverse();
	for (auto e : lt1)
	{
		cout << e << " "; //输出:1 2 3 4 5 6 7 6 5 10
	}
	cout << endl;
	return 0;
}

3. list的迭代器问题

首先我们要知道在C++中的迭代器是有不同种类的,当下阶段,迭代器大致可以分为单向迭代器,双向迭代器,随机迭代器这三种。

迭代器在某些库里的函数中常常会出现,用来表示调用该函数的容器必须要含有对应的迭代器种类(在模板定义中)才能够调用,比如算法库里的sort,只有是随机迭代器容器(如vector,string等等)才能调用。

如果我们要确认一个容器到底属于哪一种迭代器,就可以查看各个容器文档的这一页:

还有一点需要注意:

以上就是我们使用迭代器需要注意的问题了。

所以为什么在list中就不能使用算法库的sort原因了。

对于sort需要注意,在list中是使用归并排序实现的,而算法库里的sort1一般是使用快排实现的,所以list中的算法效率比较低,一般不频繁使用。所以我们常常时通过先将list中的数据拷贝到vector中去,再使用算法库里的sort进行排序,最后再将vector中的数据拷贝回来(这里拷贝的效率是非常高的,所以一般不会影响整个过程的效率)。使用示例如下:

cpp 复制代码
#include <list>
#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
	list<int> lt;
	srand(time(0));
	const int N = 100000;
	vector<int> v;
	v.reserve(N);

	for (int i = 0; i < N; ++i)
	{
		auto e = rand();
		lt.push_back(e);
	}
	int begin1 = clock();
	// 先拷贝到vector
	for (auto e : lt)
	{
		v.push_back(e);
	}

	// 排序
	sort(v.begin(), v.end());

	// 拷贝回去
	size_t i = 0;
	for (auto& e : lt)
	{
		e = v[i++];
	}

	// 完成排序
	return 0;
}

以上差不多就是list的一些基本使用情况了。


三、list的模拟实现

对list的模拟实现是我们理解list的一个重要操作。

1. 定义成员变量

我们知道list是一个带哨兵位的双向循环链表,也就是说,它是一个一个结点组成的。

我们首先需要将结点的结构实现出来,由于结点需要存储不同的数据,所以这里就需要使用模板。在C++中,这里由于结点中的变量需要再外部进行访问,所以我们可以将其通过struct实现出来,也可以是使用class实现,但需要将其设为公有的变量或者使用友元。

对于list中的变量直接是一个结点的指针,需要设为私有的。

即:

cpp 复制代码
namespace MyCreat
{
	template<class T>
	struct list_node
	{
		list_node<T>* prev;
		list_node<T>* next;
		T val;
	};
	template<class T>
	class list
	{
		typedef list_node<T> Node; //重定义一下,更加方便
	public:

		//......函数......

	private:
		Node* _head;
	};
}

2. 迭代器的实现(重点)

如果我们直接来看库里面的list的实现,会发现它是有三个模板的,可能会看不懂,如图:

现在我们就来揭秘一下 为什么会有三个模板参数?以及list中的迭代器如何实现?

(1)关键部分实现

首先,我们来实现一个迭代器的关键部分操作,能够用来完成遍历。

和之前学习的string,vector的迭代器不同,list的迭代器是不能直接加加的,因为list是链表,所以它的空间是一种不连续的,所以我们在实现迭代器时就不能简单的将结点的指针作为迭代器。

如果我们想要使用迭代器来遍历list,就必须要使用到迭代器的加加,既然list的看就看不连续,那么我们就需要实现一个list的迭代器类,来封装一下迭代器的运算,由于list对不同类型的数据的存储使用的是模板,那么实现一个迭代器类也是需要模板的。

cpp 复制代码
//实现一个迭代器的类
template<class T>
struct_list_iterator
{
	typedef list_node<T> Node;
	Node* _node;

	_list_iterator(Node* node) // 构造
		:_node(node)
	{ }

	T& operator*()
	{
		return _node->_val;
	}
	_list_iterator<T>& operator++()//重载前置++
	{
		_node = _node->_next;
		return *this;
	}

	bool operator!=(const _list_iterator<T>& it)//加const的原因是因为end是值返回,具有常性
	{
		return _node != it._node;
	}
	//通过上面就可以完成对list的遍历了
	// ....
};

在list类中,对于begin和end的函数,可以返回它们对应的结点指针:

cpp 复制代码
//返回值说明:单参构造发生了隐式类型的转换
iterator begin()
{
	return _head->_next;
}
iterator end()
{
	return _head;
}

这里两个代码需要结合者看,在上面的 bool operator!=(const _list_iterater<T>& it) 的参数加const的原因就是在遍历访问时,对于以下这种代码:

cpp 复制代码
MyCreat::_list_iterator<int> it = lt.begin();
while (it != lt.end())
{
	cout << *it << " ";
	++it;
}

因为end()是值返回,返回的是临时变量,具有常性,需要加上const修饰。

以上就是要遍历list的数据需要的迭代器的部分关键实现了

(2)const迭代器

在这里我们会解释第二个模板参数的作用。

list的const迭代器,是要求迭代器可以正常的加加,即自己可以被修改;但是迭代器指向的内容不能被修改。

所以我们的const迭代器就可以这么:将上面的操作*的返回值改为const T&。这样就可以实现一个const的迭代器了,这里我们将上面的代码拷贝一份,在将其 _list_iterater 改为_list_const_iterater ,得到下面的代码:

cpp 复制代码
//实现一个const迭代器的类
template<class T>
struct_list_const_iterator
{
	typedef list_node<T> Node;
	Node* _node;

	_list_const_iterator(Node* node) // 构造
		:_node(node)
	{
	}

	const T& operator*()
	{
		return _node->_val;
	}
	_list_const_iterator<T>& operator++()//重载前置++
	{
		_node = _node->_next;
		return *this;
	}

	bool operator!=(const _list_const_iterator<T>& it)//加const的原因是因为end是值返回,具有常性
	{
		return _node != it._node;
	}
	//通过上面就可以完成对const list的遍历了
	// ....
};

在list类中,对于begin和end的函数也需要重载一下:

cpp 复制代码
const_iterator begin() const
{
	return _head->_next;
}
const_iterator end() const
{
	return _head;
}

如此对于const的迭代器也就搞定了。

但是如果是这样写的话,就会有两段比较重复的代码,它们就是有操作*操作符时的返回值是不一样的,其他的都是一样的。所以我们这里可以简化一下,再使用一个模板参数,从而只使用一段迭代器代码来实现list的迭代器。看以下代码:

cpp 复制代码
template<class T, class Ref>
struct _list_iterator
{
	typedef list_node<T> Node;
	typedef _list_iterator<T, Ref> self; //为了下面的函数写得方便些,我们可以将该类型重定义一下
	Node* _node;

	_list_iterator(Node* node) // 构造
		:_node(node)
	{ }

	Ref operator*()
	{
		return _node->_val;
	}
	self& operator++()//重载前置++
	{
		_node = _node->_next;
		return *this;
	}

	bool operator!=(const self& it) //这里加const是因为读写权限不能放大
	{
		return _node != it._node;
	}
	//通过上面就可以完成对list的遍历了
	// ....
};

这就是第二个模板参数的使用。

(3)完善list的迭代器

在上面操作中,我们实现了普通迭代器和const的迭代器的关键操作实现。比如:重载前置++,重载*,重载!= 。而除了这三种操作,list的迭代器还有几种操作,比如:后置++,前置后置--,重载==,重载-> 等。

其中对于重载 -> ,一般重载的->操作符都是对自定义类型的数据使用的。它需要第三个模板参数。下面是对->重载的原始实现。

使用模板参数:

到这里,我们就知道了库里面的三个模板参数的由来了。

接下来我们再实现其他的重载操作,最后list的迭代器的一个类就实现完成了,如以下代码:

cpp 复制代码
template<class T, class Ref,class Ptr>
struct_list_iterator
{
	typedef list_node<T> Node;
	typedef _list_iterator<T,Ref,Ptr> self; //为了下面的函数写得方便些,我们可以将该类型重定义一下
	Node* _node;

	_list_iterator(Node* node) // 构造
		:_node(node)
	{ }

	Ref operator*()
	{
		return _node->_val;
	}
	self& operator++()//重载前置++
	{
		_node = _node->_next;
		return *this;
	}
	self operator++(int)//重载后置++
	{
		self tmp(*this);
		_node = _node->_next;
		return tmp;
	}
	self& operator--()//重载前置--
	{
		_node = _node->_prev;
		return *this;
	}
	self operator--(int)//重载后置--
	{
		self tmp(*this);
		_node = _node->_prev;
		return tmp;
	}
	bool operator!=(const self& it) //这里加const是因为读写权限不能放大
	{
		return _node != it._node;
	}
	bool operator==(const self& it) 
	{
		return _node == it._node;
	}
	Ptr operator->()
	{
		return &_node->_val;
	}
};

有了 list 的迭代器的认识之后,我们实现后面的list的基本操作就简单了。

3. 插入

list 的插入重载的实现,就是通过insert来实现的。

这里我们实现第一个就可以了,其他的都是类似的。

第一个的意思是在position位置之前插入一个val ,返回新插入结点的迭代器。

下面是insert的实现:

cpp 复制代码
// pos位置之前插入val
iterator insert(iterator pos, const T& val)
{
	Node* cur = pos._node;
	Node* prev = cur->_prev;
	Node* newnode = new Node(val);

	newnode->_prev = prev;
	prev->_next = newnode;

	newnode->_next = cur;
	cur->_prev = newnode;

	return newnode;
}

有了insert之后,对于头插和尾插就都一样调用insert1就可以了:

cpp 复制代码
void push_back(const T& x)
{
	insert(end(), x);
}

void push_front(const T& x)
{
	insert(begin(), x);
}

4. 删除

list中的删除操作是erase。

库里面有两个删除的重载,我们这里只实现第一个,即删除position位置的结点,返回下一个位置的迭代器:

cpp 复制代码
//删除pos位置的结点
iterator erase(iterator pos)
{
	assert(pos!=end());//检查pos是否有效
	Node* cur = pos._node;
	Node* prev = cur->_prev;
	Node* next = cur->_next;

	prev->_next = next;
	next->_prev = prev;
	delete cur;

	_size--;
	return next;
}

同样,通过erase也可以实现尾插和头删:

cpp 复制代码
void pop_back()
{
	erase(--end());
}

void pop_front()
{
	erase(begin());
}

5. 构造&析构

现在我们实现list的构造函数。我们实现两个常用的构造函数:默认构造和拷贝构造。即:

cpp 复制代码
list() // 构造
{
	_head = new Node;
	_head->_prev = _head;
	_head->_next = _head;
}

list(const list<T>& x) // 拷贝构造
{
	_head = new Node;
	_head->_prev = _head;
	_head->_next = _head;

	for (auto& e : x)
	{
		push_back(e);
	}
}

析构函数需要遍历链表,释放各个结点的空间,清理内部的指针(prev和next设为空)。这里我们可以使用另一种方法:使用clear函数。

clear是用来清空链表中的所有元素的,所以析构函数的实现为:

cpp 复制代码
void clear()
{
	iterator it = begin();
	while (it != end())
	{
		it=erase(it);//防止迭代器失效
	}
}
~list()
{
	clear();
	delete _head;
	_head = nullptr;
}

6. 求list中的数据个数

要求list中的数据个数,一种方法是可以直接遍历,通过计数来求;一种方法是通过在list在成员变量里设置一个变量专门来计数。在遇到插入时就加一,遇到删除就减一。很明显第一种方法是有效率的问题的。所以我们下面使用的是第二种。

C++11之前的是O(n),即通过遍历来求size;C++11之后是O(1),在list中缓存一个size变量。

cpp 复制代码
size_t size()
{
	//遍历求size
	//size_t sz = 0;
	//iterator it = begin();
	//while (it != end())
	//{
	//	++sz;
	//	++it;
	//}
	//return sz;

	return _size;
}

在上述的所有操作中,需要修改的插入或删除操作有:


四、总代码参考

cpp 复制代码
#pragma once
#include <assert.h>
namespace MyCreat
{
	template<class T>
	struct list_node
	{
		list_node<T>* _prev;
		list_node<T>* _next;
		T _val;

		list_node(const T& val = T())
			: _prev(nullptr)
			, _next(nullptr)
			, _val(val)
		{ }
	};
	
	/*
	//实现一个迭代器的类
	template<class T>
	struct _list_iterator
	{
		typedef list_node<T> Node;
		Node* _node;
		_list_iterator(Node* node) // 构造
			:_node(node)
		{ }

		T& operator*()
		{
			return _node->_val;
		}
		_list_iterator<T>& operator++()//重载前置++
		{
			_node = _node->_next;
			return *this;
		}

		bool operator!=(const _list_iterator<T>& it)//加const的原因是因为end是值返回,具有常性
		{
			return _node != it._node;
		}
		//通过上面就可以完成对list的遍历了
		// ....
	};

	//实现一个const迭代器的类
	template<class T>
	struct _list_const_iterator
	{
		typedef list_node<T> Node;
		Node* _node;
		_list_const_iterator(Node* node) // 构造
			:_node(node)
		{
		}

		const T& operator*()
		{
			return _node->_val;
		}
		_list_const_iterator<T>& operator++()//重载前置++
		{
			_node = _node->_next;
			return *this;
		}

		bool operator!=(const _list_const_iterator<T>& it)//加const的原因是因为end是值返回,具有常性
		{
			return _node != it._node;
		}
		//通过上面就可以完成对const list的遍历了
		// ....
	};
	*/
	

	template<class T, class Ref,class Ptr>
	struct _list_iterator
	{
		typedef list_node<T> Node;
		typedef _list_iterator<T,Ref,Ptr> self; //为了下面的函数写得方便些,我们可以将该类型重定义一下
		Node* _node;
		
		_list_iterator(Node* node) // 构造
			:_node(node)
		{ }

		Ref operator*()
		{
			return _node->_val;
		}
		self& operator++()//重载前置++
		{
			_node = _node->_next;
			return *this;
		}
		self operator++(int)//重载后置++
		{
			self tmp(*this);
			_node = _node->_next;
			return tmp;
		}
		self& operator--()//重载前置--
		{
			_node = _node->_prev;
			return *this;
		}
		self operator--(int)//重载后置--
		{
			self tmp(*this);
			_node = _node->_prev;
			return tmp;
		}

		bool operator!=(const self& it) //这里加const是因为读写权限不能放大
		{
			return _node != it._node;
		}
		bool operator==(const self& it) 
		{
			return _node == it._node;
		}
		Ptr operator->()
		{
			return &_node->_val;
		}

	};

	template<class T>
	class list
	{
		typedef list_node<T> Node; //重定义一下,更加方便

	public:
		typedef _list_iterator<T,T&,T*> iterator;
		typedef _list_iterator<T,const T&,T*> const_iterator;

		//返回值说明:单参构造发生了隐式类型的转换
		iterator begin()
		{
			return _head->_next;
		}
		iterator end()
		{
			return _head;
		}
		const_iterator begin() const
		{
			return _head->_next;
		}
		const_iterator end() const
		{
			return _head;
		}

		// ------------------------------------------------------

		// pos位置之前插入val
		iterator insert(iterator pos, const T& val)
		{
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* newnode = new Node(val);

			newnode->_prev = prev;
			prev->_next = newnode;

			newnode->_next = cur;
			cur->_prev = newnode;

			_size++;
			return newnode;
		}
		void push_back(const T& x)
		{
			insert(end(), x);
		}

		void push_front(const T& x)
		{
			insert(begin(), x);
		}

		// ------------------------------------------------------
		
		//删除pos位置的结点
		iterator erase(iterator pos)
		{
			assert(pos!=end());//检查pos是否有效
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* next = cur->_next;

			prev->_next = next;
			next->_prev = prev;
			delete cur;

			_size--;
			return next;
		}
		void pop_back()
		{
			erase(--end());
		}

		void pop_front()
		{
			erase(begin());
		}

		// ------------------------------------------------------

		list() // 构造
		{
			_head = new Node;
			_head->_prev = _head;
			_head->_next = _head;
			_size = 0;
		}

		list(const list<T>& x) // 拷贝构造
		{
			_head = new Node;
			_head->_prev = _head;
			_head->_next = _head;

			for (auto& e : x)
			{
				push_back(e);
			}
		}

		//清空元素
		void clear()
		{
			iterator it = begin();
			while (it != end())
			{
				it=erase(it);
			}
			_size = 0;
		}
		~list()
		{
			clear();
			delete _head;
			_head = nullptr;
		}

		// ------------------------------------------------------

		size_t size()
		{
			//遍历求size
			//size_t sz = 0;
			//iterator it = begin();
			//while (it != end())
			//{
			//	++sz;
			//	++it;
			//}
			//return sz;

			return _size;
		}
	private:
		Node* _head;
		size_t _size;
	};
}

总体而言,通过模拟实现list,我们那个更加清晰的认识list的底层结构,对list的运用能够更加得熟悉!

感谢各位观看!希望能多多支持!

相关推荐
无聊的小坏坏3 小时前
从零开始:C++ 多进程 TCP 服务器实战(续篇)
服务器·c++·tcp/ip
ceclar1233 小时前
C++容器queue
开发语言·c++
陈皮话梅糖@3 小时前
Speckit和Claude 的初体验
开发语言·ide
屈冠成3 小时前
C语言数组:编辑世界的坚固桥梁
c语言·开发语言·算法
启诚科技3 小时前
树上二分(树的重心)
c++·算法·二分·树的重心
读书读傻了哟4 小时前
Windows 10 下 VS Code 配置 C++ 开发环境(MinGW)
c++·windows·mingw
zzzyyy5384 小时前
STL简介
开发语言·c++
微露清风4 小时前
系统性学习C++-第七讲-string类
java·c++·学习
艾莉丝努力练剑4 小时前
【C++:继承】C++面向对象继承全面解析:派生类构造、多继承、菱形虚拟继承与设计模式实践
linux·开发语言·c++·人工智能·stl·1024程序员节