C++学习笔记(12)

目录

一.STL容器

1.array

2.vector

3.list

4.stack

5.queue

6.deque

7.priority_queue

8.set/multiset/bitset

9. map/multimap

10.initializer_list

11.tuple

二.迭代器汇总

1.容器中迭代器分类

2.拓展内容


一.STL容器

1.array

  • 使用时要包含 <array> 头文件

其实用起来很简单,只要明白他其实是一个模板类就足够了。下面自己来简单实现一下(只是让他看起来像,没有内置迭代器,后面会讲):

class myArray
{
public:
	myArray()
	{
		memory = new _Ty[size];
	}
	~myArray()
	{
		delete[] memory;
	}
	_Ty& operator[](int idex)
	{
		return memory[idex];
	}
protected:
	_Ty* memory;
};
int main()
{
	
	myArray<int, 3> arr;
	for (int i = 0; i < 3; i++)
	{
		arr[i] = i;
	}
	return 0;
}

可以定义,可以插入,但是跟标准库中的还是有很大差别,因为标准库中很多操作都是建立在迭代器之上的,前面我有粗略地提过迭代器的使用,现在来仔细地讲一讲:

  • 迭代器: 就是一个类中类,通过运算符重载通过类中类的对象去遍历容器

  • 迭代器的分类

    • 正向迭代器: iterator

      • begin();
      • end();
    • 反向迭代器: reverse_iterator

      • rbegin();
      • rend();
    • 常正向迭代器: const_iterator

      • cbegin();
      • cend();
    • 常反向迭代器:const_reverse_iterator

      • crbegin();
      • crend();
  • 按功能分类

    • 正向迭代
    • 双向迭代
    • 随机访问迭代器

根据这个,我们可以自己简单实现一下(之前推文有写过):

class myArray
{
public:
	myArray()
	{
		memory = new _Ty[size];
	}
	~myArray()
	{
		delete[] memory;
	}
	_Ty& operator[](int idex)
	{
		return memory[idex];
	}
	_Ty* begin()
	{
		return memory;
	}
	_Ty* end()
	{
		return memory + size;
	}
	class iterator
	{
	public:
		iterator(_Ty* pmove = nullptr):pmove(pmove){}
		//运算符重载
		void operator=(_Ty* pmove)
		{
			this->pmove = pmove;
		}
		bool operator!=(_Ty* pmove)
		{
			return this->pmove != pmove;
		}
		iterator operator++(int)
		{
			this->pmove++;
			return *this;
		}
		_Ty operator*()
		{
			return *pmove;
		}
	protected:
		_Ty* pmove;
	};
protected:
	_Ty* memory;
};
int main()
{
	
	myArray<int, 3> arr;
	for (int i = 0; i < 3; i++)
	{
		arr[i] = i;
	}
	myArray<int, 3>::iterator iter;
	for (iter = arr.begin(); iter != arr.end(); iter++)
	{
		cout << *iter << '\t';
	}
	return 0;
}

 可以发现,array跟数组没什么差别,所以,实际上数组也可以看作是容器。

下面是容器的一些基本操作:

class Man
{
public:
	Man(){}
	Man(string name,int age):name(name),age(age){}
	void print()
	{
		cout << name << '\t' << age << endl;
	}
protected:
	string name;
	int age;
};
int main()
{
	//可以定义时初始化
	array<int, 3> arr1 = { 1,2,3 };
	array<int, 3> arr2;
	//可以交换,但长度一定要一样长
	arr1.swap(arr2);
	//有迭代器可以使用新版本的for循环打印
	for (auto v : arr2)
	{
		cout << v << '\t';
	}
	//处理自定义类型数据
	array<Man, 3> arr3;
	for (int i = 0; i < arr3.size(); i++)
	{
		string name = "name";
		arr3[i] = Man(name + to_string(i), i);
	}
	for (auto v : arr3)
	{
		v.print();
	}
	return 0;
}

2.vector

  • 使用时要包含 <vector> 头文件
  • 使用时可以带长度,也可以不带,可以初始化,也可以不初始化

下面是各种初始化方式汇总:

//辅助函数,遍历容器
template <class _Ty>
void printVector(vector<_Ty>& temp)
{
	for (auto v : temp)
	{
		cout << v << '\t';
	}
	cout << endl;
}
int main()
{
	vector<int> vecData;
	//采用成员函数的方式做插入.插入多少个元素都可以--->会自动扩建
	for (int i = 0; i < 3; i++)
	{
		vecData.push_back(i);	//尾插法 0 1 2
	}
	printVector(vecData);

	//带初始化
	vector<double> dData = { 1.1,1.23,1.44 };  //自动推断长度为3
	printVector(dData);

	vector<string> strData(3);		//当前动态数组的长度是3
	for (int i = 0; i < 3; i++)     //确定长度,可以直接使用数组法插入
	{
		string name = "name";
		//只有在确定长度范围以内的可以直接采用数组法(下标)插入
		strData[i] = name + to_string(i);
	}
	printVector(strData);
	//strData[3] = "name3";    vector subscript out of range 引发中断
	strData.push_back("name3");	    //超过的必须用成员函数插入(会自动扩增)
	printVector(strData);

	return 0;
}

如果数据是自定义类型,那最好重载一下流运算符,老生常谈了,这里就不演示,接下来附上vector常用操作:

int main()
{
	vector<int> vec = { 1,2,3,4 };
	cout << vec.size() << endl;		//当前容器中的元素个数
	cout << vec.empty() << endl;		//判断是否为空  
	cout << vec.front() << endl;		//访问第一个元素
	cout << vec.back() << endl;		//访问最后一个元素
	cout << vec.at(2) << endl;		//直接用下标法访问,返回当前下标下面的值
	cout << vec[2] << endl;		    //和at(2)一样效果 不能&vec[2]没有重载&
	//修改某个元素,通过下标来修改
	vec.emplace(vec.begin() + 2, 100); /*在指定位置插入一个新的元素,用迭代器去指位置,传入
											 的是位置,往第2个位置插入100 */
	vec.emplace(vec.begin(), 1111);	//往第一个位置插入元素			
	printVector(vec);
	vec.emplace_back(999);			//和 push_back 一样的功能

	//vec.erase(vec.begin() + 2);   //数组只有伪删除,没有删除操作(万金油函数,链表中有用)
	printVector(vec);

	//批量复制 想把一个数组的内容拷贝到一个向量中去--->用迭代器去指定位置
	int array[] = { 1,2,3 };            //一个普通数组赋值为1,2,3
	vector<int> vecData;                //不需要起始长度--->构建的是对象,会自动释放
	vecData.assign(array, array + 3);   /*赋值的方式,把数组中的东西赋值给另一个数组(把元素的
										  第一个位置到最后一个位置拷贝到容器中)*/
	printVector(vecData);

	return 0;
}

3.list

  • 包含头文件 <list>

双向链表可以头插和尾插。

基本操作如下:

int main()
{
    list<int> iNum;
    list<string> strNum;
    //插入
    strNum.push_back("string1");    //尾插发
    strNum.push_back("string2");
    strNum.push_front("string3");
    //string3 string1  string2
    //遍历
    //不删除方式遍历
    list<string>::iterator iter;
    for (iter = strNum.begin(); iter != strNum.end(); iter++)
    {
        cout << *iter << " ";
    }
    cout << endl;
    cout << "是否为空:" << boolalpha << !strNum.empty() << endl;
    cout << "元素个数:" << strNum.size() << endl;
    //删除方式遍历
    //string3 string1  string2
    while (!strNum.empty())
    {
        cout << strNum.front() << " ";    //back()
        strNum.pop_front();                //头部删除  pop_front();
    }
    cout << endl;
    cout << "元素个数:" << strNum.size() << endl;
    //指定位置操作
    //iterator find(iterator begin,iterator end,data);
    for (int i = 0; i < 3; i++)
    {
        iNum.push_back(i);
    }
    auto result = find(iNum.begin(), iNum.end(), 2);//find返回的是迭代器
    //没找到返回是end结束的位置
    if (result == iNum.end())
    {
        cout << "未找到指定位置" << endl;
    }    
    //insert:在指定的迭代器前插入,返回指向插入数据的迭代器
    iNum.insert(result, 100);
    //删除函数:使作为参数的迭代器失效,并返回指向该迭代器下一参数的迭代器
    iNum.erase(result);
    //其他操作
    iNum.reverse();                //反转链表
    iNum.sort(less<int>());                //排序
	return 0;
}

其中要注意的是erase函数,如果用它做删除,应该这么写:

int main()
{
	int array[4] = { 1,2,2,3 };
	list<int>data;
	data.assign(array, array + 4);  //初始化list---把数据拷贝到data中,把2全部删除
		//相同元素的删除
	for (list<int>::iterator iter = data.begin(); iter != data.end(); )
	{
		if (*iter == 2)
		{
			iter = data.erase(iter);  //函数返回下一个迭代器
		}
		else
		{
			iter++;
		}
	}
	return 0;
}

4.stack

  • 包含头文件 <stack>

使用方式很简单,先进后出,可以用在游戏数据保存,实现回退功能。下面是常用的方法:

int main()
{
    stack<int> intStack;
    for (int i = 0; i < 3; i++)
    {
        intStack.push(i);//入栈
    }
    while (!intStack.empty())
    {
        cout << intStack.top() << "\t";
        intStack.pop();//出栈
    }
	return 0;
}

5.queue

  • 包含头文件 <queue>

先进先出,跟栈相反。

int main()
{
    queue<int> qData;
    for (int i = 0; i < 3; i++)
    {
        qData.push(i);    //入队
    }
    while (!qData.empty())
    {
        cout << qData.front() << " ";  //获取队头元素
        qData.pop();    //出队
    }
    cout << endl;
    cout << qData.size() << endl;
	return 0;
}

6.deque

  • 包含头文件 <deque>

双向队列,头尾能进能出

//从头出来
void pop_front_dequeue(deque<int> dData)
{
    while (!dData.empty())
    {
        cout << dData.front() << " ";
        dData.pop_front();
    }
    cout << endl;
}
//从尾出来
void pop_back_dequeue(deque<int> dData)
{
    while (!dData.empty())
    {
        cout << dData.back() << " ";
        dData.pop_back();
    }
    cout << endl;
}
int main()
{
    deque<int> deData;
    for (int i = 0; i < 3; i++)
    {
        deData.push_back(i);        //尾插法入队
        deData.push_front(i);        //头插法入队
    }
    deData.push_back(999);
    //0 0
    //1 0 0 1
    //2 1 0 0 1 2
    pop_front_dequeue(deData);
    pop_back_dequeue(deData);
	return 0;
}

7.priority_queue

  • 包含头文件 <queue>

优先队列,有自动排序,默认大的先出队

//默认的方式,一级下面三种 都一样,大的先出队
    priority_queue<int> pqData;
    priority_queue<int, vector<int>> pqData2;            //默认排序准则
    priority_queue<int, vector<int>, less<int>> pqData3;    //所有参数都完整
    //优先队列,是按照数据的优先权出队,VIP服务
    pqData.push(12);
    pqData.push(0);
    pqData.push(34);
    while (!pqData.empty())
    {
        cout << pqData.top() << " ";
        pqData.pop();        //出队
    }
    cout << endl;
    //贪心算法
    priority_queue<int, vector<int>, greater<int>> pqDataG;
    pqDataG.push(12);
    pqDataG.push(0);
    pqDataG.push(34);
    while (!pqDataG.empty())
    {
        cout << pqDataG.top() << " ";
        pqDataG.pop();        //出队
    }
    cout << endl;

8.set/multiset/bitset

  • 包含头文件 <set>/<bitset>
  • set集合:1.数据自带排序;2.数据具有唯一性
  • multiset:只有排序,没有数据唯一性
  • bitset:二进制位集合
class MM 
{
public:
    MM(string name, int age) :name(name), age(age) {}
    bool operator<(const MM& object)const 
    {
        return this->name < object.name;
    }
    void print() 
    {
        cout << name << " " << age << endl;
    }
protected:
    string name;
    int age;
};

void testSet() 
{
    srand((unsigned int)time(nullptr));
    set<int> setData;                    //默认方式 从小到大
    set<int, less<int>> setData2;        //和默认方式一样
    set<int, greater<int>> setData3;    //从大到小
    int array[10] = { 0 };
    for (int i = 0; i < 10; i++)
    {
        int temp = rand() % 10;
        array[i] = temp;
        setData.insert(temp);
    }
    for (auto v : array) 
    {
        cout << v << " ";
    }
    cout << endl;
    for (set<int>::iterator iter = setData.begin(); iter != setData.end(); iter++) 
    {
        cout << *iter << " ";
    }
    cout << endl;
}
void testUserData() 
{
    set<MM> mmData;   //less<int> <
    mmData.insert(MM("name3", 19));
    mmData.insert(MM("name2", 28));
    mmData.insert(MM("name3", 188));
    for (auto v : mmData) 
    {
        v.print();
    }

    set<char> cData;
    set<string> strData;
}
//多重集合: 只具有排序功能,不具有去重功能
void testmultiset()
{
    multiset<int> mulData;
    for (int i = 0; i < 10; i++) 
    {
        mulData.insert(rand() % 10);
        //rand()%26+'A';
        //rand()%26+'a';
    }
    for (auto v : mulData) 
    {
        cout << v << " ";
    }
    cout << endl;
}
int main() 
{
    testSet();
    testUserData();
    testmultiset();


    return 0;
}

bitset的使用比较有有趣:尖括号的参数是位数

int main()
{
    //多个二进制位
    bitset<8> bData("11110000");
    cout << bData << endl;
    bData.flip();
    cout << bData << endl;
    cout << bData.all() << endl;
    cout << bData.any() << endl;
    cout << bData.size() << endl;
    cout << bData.none() << endl;
    bitset<8> num(7);
    cout << num << endl;
    return 0;
}

9. map/multimap

  • 包含头文件 <map>
  • map储存的是数对类型的数据
  • map自带排序,默认是从小到大,也具有数据唯一性
  • 可以用map映射进行资源的综合管理,增强代码可读性
  • 第一个参数对map有决定性作用,比较时通过它来进行
  • map[first]=second;
int main()
{
    map<int, string> mapData;
    map<int, string, less<int>> mapData1; //和上面创建方式一样,从小到大
    map<int, string, greater<int>> mapData2; //从大到小
    //1.insert插入
    mapData.insert(pair<int, string>(1, "string"));
    //2.make_pair构建数对插入
    mapData.insert(make_pair<int, string>(3, "string3"));
    //3.单映射,可以直接采用数组下标方式进行插入
    //数组在一定程度上来说可以看作映射
    //map[first]=second;
    //相比数组来说,这个下标是没有任何要求
    mapData[-1] = string("string-1");  //等效插入一个数对类型
    //上面代码等效:mapData.insert(pair<int,string>(-1,"string-1"))
    mapData[1] = "string1";        //相同键 采用的是覆盖方式
    //遍历:
    for (auto iter = mapData.begin(); iter != mapData.end(); iter++)
    {
        //*iter指的是pair类型
        cout << iter->first << " " << iter->second << endl;
    }
    for (auto v : mapData)
    {
        cout << v.first << "\t" << v.second << endl;
    }
    //map<string, IMAGE*> img;
    //img["墙"] = new IMAGE;
    //img["路"] = new IMAGE;
    //putimage(0, 0, img["墙"]);
    //putimage(0, 0, img["路"]);
    cout << mapData[1] << endl;    //用的时候直接使用即可
    mapData.erase(1);
    for (auto v : mapData)
    {
        cout << v.first << "\t" << v.second << endl;
    }
    cout << endl;
    return 0;
}

多重映射没什么限制,什么样的关系都能插入到映射之中,因为可能存在相同的first参数,所以不能采用下标法进行访问。

int main()
{
    multimap<int, string>  mulData;
    mulData.insert(pair<int, string>(1, "string"));
    mulData.insert(pair<int, string>(1, "string1"));
    mulData.insert(pair<int, string>(2, "string"));
    mulData.insert(pair<int, string>(3, "string"));
    mulData.insert(make_pair<int, string>(3, "string"));
    for (auto v : mulData)
    {
        cout << v.first << "\t" << v.second << endl;
    }
    return 0;
}

10.initializer_list

  • 包含头文件 <initializer_list>
  • 列表,可无限增长,所以多用为函数参数,这样可以做到无限制的参数个数。
void print(initializer_list<int> list)
{
    for (auto iter = list.begin(); iter != list.end(); iter++)
    {
        cout << *iter << " ";
    }
    cout << endl;
}
int main()
{
    array<int, 3> arrData = { 1,2,3 };
    vector<int> vecData1 = { 1,2,3,4 };
    vector<int> vecData2 = { 1,2,3,4,5 };
    initializer_list<int> listOne = { 1,2,3,4,5 };
    initializer_list<int> listTwo = { 1,2,3 };
    print({ 1 });
    print({ 1,2 });
    print({ 1,2,3,4 });
    print({ 1,2,3,4,5 });
    return 0;
}

11.tuple

  • 包含头文件 <tuple>
  • 可以把它当成结构体去用,很方便
  • 访问方式比较特殊,不能用for循环,用get方法,或者是tie访问数据储存后,再用流的形式打印出来。
void testTuple()
{
    //把任何类型的一系列数据当做一组处理
    tuple<string, int, int, string> mmInfo = { "MM",18,1001,"123445" };
    tuple<double, double, double> mmscore = make_tuple(98, 98, 88);
    tuple<string, string> value = forward_as_tuple("小张", "小美");
    tuple<string, int, int, string> array[3];
}
void visitedData()
{
    tuple<string, int, int, string> mmInfo = { "MM",18,1001,"123445" };
    //get方法,不能用for循环
    cout << get<0>(mmInfo) << "\t";
    cout << get<1>(mmInfo) << "\t";
    cout << get<2>(mmInfo) << "\t";
    cout << get<3>(mmInfo) << endl;
    //tie的方式访问数据
    string name;
    int age;
    int num;
    string tel;
    tie(name, age, num, tel) = mmInfo;
    cout << name << "\t" << age << "\t" << num << "\t" << tel << endl;
}
void Exoperator()
{
    tuple<string, int, int, string> mmInfo = { "MM",18,1001,"123445" };
    tuple<double, double, double> mmscore = make_tuple(98, 98, 88);
    //合并两个元组
    auto result = tuple_cat(mmInfo, mmscore);
}
int main()
{

    visitedData();
    return 0;
}

二.迭代器汇总

1.容器中迭代器分类

容器名迭代器类型
array随机访问
vector随机访问
deque随机访问
stack/queue/priority_queue(特定顺序存取)不支持
list双向
set/multiset双向
map/multimap双向

2.拓展内容

  • 迭代器辅助函数

    • 移动:advance(iterator iter,n);
    • 间距:distance(iterator begin,iterator end);
    • 交换:iter_swap(iterato first,iterator end);
  • 拓展内容:特殊迭代器 流型迭代器-->一般用在辅助打印

    • 输出流型
      • ostream_iterator<_Ty> iter(ostream& out);
      • ostream_iterator<_Ty> iter(ostream& out,char* str);
      • 输出流型迭代做赋值运算,意味着就是打印数据到屏幕上
    • 输入流型
      • istream_iterator<_Ty> iter; //构造无参对象,是一个错误流 end_of_ostream
      • istream_iterator<_Ty> iter(istream& in);
      • *iter 等效cin>>操作
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值