首页 > 编程语言 >C++《list》

C++《list》

时间:2024-10-31 23:17:16浏览次数:3  
标签:lt1 emplace int list back C++

在本篇当中我们将学习STL中的list,在此list就是我们之前在数据结构学习过的链表,在本篇中我们要来了解list当中的成员函数该如何使用,由于list各个函数的接口和之前学习过的vector类型,因此在学习list的使用就较为轻松。在lis篇章中我们要重点了解的是在下一个篇章当中的list模拟实现中的迭代器实现,由于list底层的物理空间不一定是连续的,因此list迭代器的实现相比之前学习过的容器就复杂多了,在下一篇中将带来细致的讲解。在此之前我们先来了解list该如何使用吧!!


1.构造函数

queue - C++ Reference

通过以上的文档所示就可以看出list类提供的构造函数接口和之前我们学习过的vector非常的相识,都实现了迭代器区间构造;n个指定元素的构造;使用initializer_list实现构造;拷贝构造

例如以下实现:

#include<iostream>
#include<list>
#include<string>
using namespace std;


int main()
{
	string a("abcdef");
	list<int> lt1;
	list<int> lt2({ 1,2,3,4,5 });
	list<int> lt3(a.begin(), a.end());
	list<int> lt4(10, 9);
    list<int> lt5(lt2);
	
	return 0;
}

 

2.析构函数

queue - C++ Reference

在list类当中也实现了析构函数,在此该函数不需要我们在使用list时显示调用,这是由于析构函数编译器会在list对象销毁时自动调用

3.赋值运算符重载

list::operator= - C++ Reference

在list实现了赋值运算符重载的函数,这就使得将一个已经初始化的list对象赋值给另一个已经初始化的list对象

例如以下示例:

#include<iostream>
#include<list>
using namespace std;


int main()
{
	list<int> lt1;
	list<int> lt2({ 1,2,3,4,5 });
	
	lt1 = lt2;

	return 0;
}

 

4. 容量操作

在list当中由于和之前学习的string、vector不同在存储数据时,每存储一个指定的元素就申请相应的对应合适的内存空间,因此在list就不会提前将空间开好,这就使得list当中容量操作相关的函数只有以下所示:

4.1 size

list::size - C++ Reference 

在list提供了用于得到list对象内有效元素个数的成员函数size,到了这里你就可以了解到为什么之前在之前string当中得到有效元素建议使用size而不是length,这就使因为size在其他STL当中也是有提供的,而length只是string特有的,都使用size就利于记忆

4.2 empty

list::empty - C++ Reference

在list也提供用于判断list对象内有效元素是否为空的函数empty,当为空时就返回true,否则就为flase

5. 迭代器

在list类当中由于底层存储数据的内存空间不像string和vector一样物理结构是连续的,因此在list就没有提供下标+[ ]的方式实现对list对象内元素的访问,因此在list就只能使用迭代器来实现

在此list内的迭代器和之前学习过的容器一样也提供了普通迭代器和反向迭代器,并且在这些当中都实现了const和非const版本 

5.1 begin与end

list::begin - C++ Reference

在此list内提供的begin函数还是指向对象的第一个元素,返回值为迭代器

list::end - C++ Reference

在此list内提供的end函数还是指向对象的最后一个元素后一个位置,返回值为迭代器

有了begin()和end()就可以实现对list对象遍历,例如以下示例:

#include<iostream>
#include<list>
using namespace std;


int main()
{

	list<int> lt2({ 1,2,3,4,5 });
	
	lt1 = lt2;

	list<int>::iterator t1 = lt1.begin();
	while (t1 != lt1.end())
	{
		(*t1)++;
		t1++;
	}
	t1 = lt1.begin();
	while (t1 != lt1.end())
	{
		cout << *t1 << " ";
		t1++;
	}
	cout << endl;

	return 0;
}

 

6.范围for

在此在list由于实现了迭代器那么就可以使用范围for来对list对象遍历其的元素

例如以下示例:

#include<iostream>
#include<list>
using namespace std;

int main()
{
	list<int> lt1;
	lt1 = lt2;
	for (auto x : lt1)
	{
		cout << x << " ";
	}
	cout << endl;
	
	return 0;
}

 

7.取头尾元素

list::front - C++ Reference

list::back - C++ Reference 

在list提供了front函数和back函数来分别实现得到list对象内的首元素与尾元素 

例如以下示例:

#include<iostream>
#include<list>
using namespace std;

int main()
{
	list<int> lt2({ 1,2,3,4,5 });
	cout << "lt2front:" << lt2.front()<<endl;
	cout << "lt2back:" << lt2.back() << endl;

	return 0;
}

 

8.元素修改操作

在list当中提供了以上所示的各种用于修改list对象内元素的函数

8.1 push_front与pop_front

list::push_front - C++ Reference


list::pop_front - C++ Reference

 在list内实现了push_front和pop_front来放分别实现在对象内头插和头删元素
在此要了解到为什么在list会提供该函数而之前学习的string与vector都未实现该函数,这是由于在在链表当中头插和头删元素不需要像顺序表一样在头插和头删时将大量的元素移动,这样就不会出现效率低下的问题

8.2 push_back与pop_back

list::push_back - C++ Reference

 list::pop_back - C++ Reference

 在list内实现了push_back和pop_back来放分别实现在对象内尾插和尾删元素

8.3 insert和erase

list::insert - C++ Reference

list::erase - C++ Reference

在list也提供了inert和erase来实现任意位置的插入和删除,在此实现的接口和vector相识参数都是迭代器或者迭代器区间 

8.4 emplace_front与emplace_back

 list::emplace_front - C++ Reference

list::emplace_back - C++ Reference

在C++11之后实现了以上的两个函数emplace_front和emplace_back,那么这两个函数有什么作用呢?

在此你可以认为emplace_front和emplace_back与push_front和push_back的功能是类似的,功能分别是在list对象头插和尾插指定的数据,但其实这两个函数与push_front和push_back还是有差异的,接下来就来大体的讲解

注:在此emplace_front和emplace_back函数的参数涉及到可变模板参数、右值引用等概念在此不进行讲解,这些要到之后C++11篇章才进行

通过之前的学习我们知道push_front和push_back是可以实现在相应的类对象内插入内置类型的数据,也可以插入自定义类型的数据;并且在此支持隐式类型转换

例如以下示例:

#include<iostream>
#include<list>
using namespace std;

class Postion
{
public:
	Postion(int row,int col)
		:_row(row)
		,_col(col)
	{

	}

private:
	int _row;
	int _col;
};


int main()
{
	list<Postion> lt1;
	Postion p1(1,2);
	lt1.push_back(p1);
    //使用匿名对象
	lt1.push_back(Postion(1, 2));
    //使用多参数隐式类型转换
	lt1.push_back({ 1, 2 });

	return 0;
}

那么以上使用emplace_back不同的参数也可以使用吗

#include<iostream>
#include<list>
using namespace std;

class Postion
{
public:
	Postion(int row,int col)
		:_row(row)
		,_col(col)
	{

	}

private:
	int _row;
	int _col;
};

int main()
{
	list<Postion> lt1;
	Postion p1(1, 2);
	lt1.emplace_back(p1);
	lt1.emplace_back(Postion(1, 2));
	lt1emplace_back({ 1, 2 });


	return 0;
}

以上代码在VS下就会出现以下的编译报错,这是为什么呢?


 

要解决以上的问题就先了解到emplace_back的形参的类型是根据实参的类型来推导的,那么在以上使用隐式类型转换形参就无法根据实参{ 1, 2 }来推导具体的类型,这里具体的原因就是形参是模板

在此emplace_back和emplace_front最大的特点是支持直接将构造对象的参数直接传emplace_back和emplace_front函数的参数

因此以上代码正确的使用方法是如下所示:

#include<iostream>
#include<list>
using namespace std;

class Postion
{
public:
	Postion(int row,int col)
		:_row(row)
		,_col(col)
	{

	}
private:
	int _row;
	int _col;
};


int main()
{
	list<Postion> lt1;
	Postion p1(1,2);
	lt1.push_back(p1);
	lt1.push_back(Postion(1, 2));
	lt1.push_back({ 1, 2 });

	list<Postion> lt1;
	Postion p1(1, 2);
	lt1.emplace_back(p1);
	lt1.emplace_back(Postion(1, 2));

	//lt.1emplace_back({ 1, 2 });
	lt1.emplace_back( 1, 2 );

	return 0;
}

注:在此由于emplace_back是直接构造去初始化节点,而push_back需要通过构造外加拷贝实现的,因此emplace_back相比push_back效率更高

9. list内特有的操作

在list中提供了以上特有的函数来实现逆置、排序等的操作,这些操作是之前我们在string和vector当中没有提供的,那么接下来我们就来了解这些函数的作用以及使用方法

9.1 splice

list::splice - C++ Reference

在此splice的作用是将一个list对象的值转移给另一个list的对象,并且转移之后被转移对象会变为空

例如以下示例:

#include<iostream>
#include<list>
using namespace std;

int main()
{

	list<int> mylist1, mylist2;
	list<int>::iterator it;

	// set some initial values:
	for (int i = 1; i <= 4; ++i)
		mylist1.push_back(i);      // mylist1: 1 2 3 4

	for (int i = 1; i <= 3; ++i)
		mylist2.push_back(i * 10);   // mylist2: 10 20 30

	it = mylist1.begin();
	++it;                         // points to 2

	mylist1.splice(it, mylist2); // mylist1: 1 10 20 30 2 3 4
	// mylist2 (empty)
	// "it" still points to 2 (the 5th element)


	return 0;
}

标签:lt1,emplace,int,list,back,C++
From: https://blog.csdn.net/2303_81098358/article/details/143378228

相关文章

  • C++ 手撕--基本数据结构的简单实现
    C++面试手撕代码----基本数据结构的简单实现1.String数据结构的简单实现:#include<iostream>#include<cstring>//forstrcpystrlenmethodsusingnamespacestd;classString{private: char*data; size_tlength;public: String():data(nullptr),length(0)......
  • 华为OD机试-(E卷,100分) - 补种未成活胡杨(Java & Python& JS & C++ & C )
    最新华为OD机试题目描述近些年来,我国防沙治沙取得显著成果。某沙漠新种植N棵胡杨(编号1-N),排成一排。一个月后,有M棵胡杨未能成活。现可补种胡杨K棵,请问如何补种(只能补种,不能新种),可以得到最多的连续胡杨树?输入描述N总种植数量,1<=N<=100000M未成活胡杨数量,M个空格......
  • C++对象优化4条原则
    1、函数参数传递优先使用传引用,而不是传值①、函数参数传递的过程是赋值的过程,对象之间赋值是会产生赋值运算符的重载调用,退出函数时还会再调用一次析构函数,传引用就不存在上述函数调用2、函数返回一个临时对象时,应该直接返回,而不应该先定义一个临时对象,然后返回定义的临......
  • C++笔记---可变参数模板
    1.简单介绍与基本语法可变参数模板是指模板的类型参数列表的的参数个数可变。C++11支持可变参数模板,也就是说支持可变数量参数的函数模板和类模板,可变数目的参数被称为参数包,存在两种参数包:模板参数包:表示零或多个模板参数。函数参数包:表示零或多个函数参数。参数包的......
  • 深入计算机语言之C++:内存管理
    ......
  • C++三五法则
     若类中有资源在构造函数中创建,并在析构函数中释放,此时需要显式定义拷贝构造、赋值,析构等操作,若在程序没有显示声明并定义时,会被隐式生成,对于不包含联合体的类,隐式生成的拷贝构造函数和赋值运算在执行时,会按成员对象依次复制,隐式生成的析构函数为空如下面的类T管理资源int*clas......
  • C++泛型一:模板
    数据类型给程序设计带来的困扰及解决方案intmaxt(int,int);doublemaxt(double,double);若有一种占位符T,能够代替类型,便可以简化代码的冗余编写Tmaxt(T,T);C++模板模板声明如下template<typenameT1,...>template是C++的模板声明关键字,尖括号内为模板参数列表typ......
  • C++范型二:右值引用
    为类所设计的转移语义拷贝构造函数和转移语义赋值运算符使得临时对象有了将资源直接转移给另一个对象的能力,从而避免了内存分配、资源拷贝等深拷贝过程作为注重效率的模板,当然要引入右值引用及相关技术,其成果就是参数完美转发模板右值引用左值和右值左值代表一块存储空间,可以......
  • C++范型三:数据类型表
    类的数据类型成员C++中,在类模板中用typedef定义的数据类型称为内嵌类型nestedtypetemplate<typenameT>classMyTraits{public:typedefTmytype;};如下,使用内嵌类型在类外定义变量typenameMyTraits<int>::mytypen;可知,类外引用类模板的公有类型成员和引用类静......
  • C/C++中的指针详解(重点)
    指针是C和C++中一个重要且强大的特性。它们允许程序员直接访问和操作内存,提供了灵活的内存管理和高效的数据结构实现。对一个变量取*操作其实就是取到这个变量的地址,然后再对取到的变量进行读写等操作以下是对指针的详细介绍:1.什么是指针指针是一个变量,它存储另一个变量的......