c++基础知识汇总(四) STL容器 | 类初始化| auto变量

目录

一、set集合

1.1 使用

1.2 插入与遍历

1.3 删除

1.4 查找

1.5 运用及结果

二、list

2.1 常用插入与删除

三、类的初始化

3.1 看程序说结果

3.2 类初始化顺序

四、auto变量

4.1 C98中的auto

4.2 C++11的auto

4.3 应用实例

4.4 auto注意事项

4.5 个人应用


一、set集合

1.1 使用

#include<set>

set头文件可以保证输入不重复,输出为0到15

int main(){
	Solution Solution;
	int num; num = 10;
	set<int> int_set;
	while (num--){
		int_set.insert(num);
	}
	for (int idx = 0; idx < 15; idx++){
		int_set.insert(idx);
	}
	
	for (auto item : int_set){
		cout << item << " ";
	}
	int end; cin >> end;
	return 0;
}
// 输出 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14

1.2 插入与遍历

插入用.insert,

遍历直接可以auto 即可

1.3 删除

set.erase

std::set<int> numbers {2, 4, 6, 8, 10, 12, 14};
auto iter = numbers.erase(++std::begin(numbers));
auto n = numbers.erase(12);
n = numbers.erase(13);
numbers.clear();

1.4 查找

set.find

找到返回set的元素的iterator

找不到返回set.end()

std::set<string> words {"one", "two","three", "four","five"};
auto iter = words.find ("one") ; // iter points to "one"
iter = words.find(string{"two"});   // iter points to "two"
iter = words.find ("six");   // iter is std:: end (words)

1.5 运用及结果

#include<iostream>
#include<vector>
#include<string>
#include<set>
using namespace std;

int main(){
	set<int> set_list = {1,2,3,4,5,6};
	set_list.insert(1);
	set_list.insert(2);
	set_list.insert(3);
	set_list.erase(1);
	set_list.erase(3);
	set_list.erase(5);
	auto item = set_list.find(4);
	if (item == set_list.end()){
		cout << "not exist!" << endl;
	}
	else cout << "Exist!" << endl;
	
	for (auto it : set_list){
		cout << it << " ";
	}
	cout << endl;

	int end; cin >> end;
	return 0;
}

输出:

Exist!
2 4 6

二、list

https://blog.csdn.net/leo_888/article/details/80772146

c++中的list会被初始化为双向链表。可以实现方便的插入与删除。

但是不能取地址,比如下面这种操作是不行的:报错,除非将a初始化为vector

//错误,list无法随机访问
	list<int> a;
        for (int idx = 0; idx < 5; idx++){
		cout << a[idx] << " ";
	}
//错误,list相当于双向链表,无法用<
	for (auto iter = a.begin(); iter < a.end();iter++){
		cout << *(a.begin()) << " ";
	}
//正确,只能一次次往下迭代
	for (auto iter = a.begin(); iter != a.end();iter++){
		cout << *(a.begin()) << " ";
	}

2.1 常用插入与删除

push_back与push_front

int main(){
	list<int> a;
	for (int idx=0; idx < 10; idx++){
		a.push_back(idx);
		a.push_front(6);
	}

	for (auto item: a){
		cout << item << " ";
	}
	cout << endl;

	while (*(a.begin()) == 6){
		a.pop_front();
	}

	for (auto item : a){
		cout << item << " ";
	}
	cout << endl;

	int end; cin >> end;
	return 0;
}

输出结果:

6 6 6 6 6 6 6 6 6 6 0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9

三、类的初始化

3.1 看程序说结果

class A
{
private:
	int n1;
	int n2;
public:
	A() :n2(0), n1(n2 + 2)
	{}
	void Print(){
		std::cout << n1<<" "<< n2 << std::endl;
	}

};
int main(){
	A a;
	a.Print();
	A* ptr = new A();
	ptr->Print();
	int end; cin >> end;
	return 0;
}

运行结果:

输出:
-858993458 0
-842150449 0

原因:

  • 类中的初始化顺序只与类中声明顺序有关

即按照下面顺序,先初始n1,再初始n2

private:
    int n1;
    int n2;

所以先用n2+2初始化n1,是随机的,然后再用0初始化n2。因此得到上面输出

3.2 类初始化顺序

变量初始化顺序:https://blog.csdn.net/weixin_40087851/article/details/82346473

·  从全局看,变量的初始化顺序如下:

  • 基类的静态变量或全局变量。
  • 派生类的静态变量或全局变量。
  • 基类的成员变量。
  • 派生类的成员变量。

四、auto变量

https://www.cnblogs.com/KunLunSu/p/7861330.html

4.1 C98中的auto

无用,可不看。了解即可。C++11完全舍弃了这种用法。

C++98标准中就存在了auto关键字,那时的auto用于声明变量为自动变量,自动变量意为拥有自动的生命期,这是多余的,因为就算不使用auto声明,变量依旧拥有自动的生命期:

int a =10 ;  //拥有自动生命期
auto int b = 20 ;//拥有自动生命期
static int c = 30 ;//延长了生命期

4.2 C++11的auto

auto可以在声明变量的时候根据变量初始值的类型自动为此变量选择匹配的类型,类似的关键字还有decltype。举个例子:

    int a = 10;
    auto au_a = a;//自动类型推断,au_a为int类型
    cout << typeid(au_a).name() << endl;

typeid运算符可以输出变量的类型。程序的运行结果输出了

int

4.3 应用实例

上面举的这个例子很简单,在真正编程的时候也不建议这样来使用auto,直接写出变量的类型更加清晰易懂。下面列举auto关键字的正确用法。

用于代替冗长复杂、变量使用范围专一的变量声明。

想象一下在没有auto的时候,我们操作标准库时经常需要这样:

#include<string>
#include<vector>
int main()
{
    std::vector<std::string> vs;
    for (std::vector<std::string>::iterator i = vs.begin(); i != vs.end(); i++)
    {
        //...
    }
}

这样看代码写代码实在烦得很。有人可能会说为何不直接使用using namespace std,这样代码可以短一点。实际上这不是该建议的方法(C++Primer对此有相关叙述)。使用auto能简化代码:

#include<string>
#include<vector>
int main()
{
    std::vector<std::string> vs;
    for (auto i = vs.begin(); i != vs.end(); i++)
    {
        //..
    }
}

for循环中的i将在编译时自动推导其类型,而不用我们显式去定义那长长的一串。

在定义模板函数时,用于声明依赖模板参数的变量类型。

template <typename _Tx,typename _Ty>
void Multiply(_Tx x, _Ty y)
{
    auto v = x*y;
    std::cout << v;
}

若不使用auto变量来声明v,那这个函数就难定义啦,不到编译的时候,谁知道x*y的真正类型是什么呢?

模板函数依赖于模板参数的返回值

template <typename _Tx, typename _Ty>
auto multiply(_Tx x, _Ty y)->decltype(x*y)
{
    return x*y;
}

当模板函数的返回值依赖于模板的参数时,我们依旧无法在编译代码前确定模板参数的类型,故也无从知道返回值的类型,这时我们可以使用auto。格式如上所示。
decltype操作符用于查询表达式的数据类型,也是C++11标准引入的新的运算符,其目的也是解决泛型编程中有些类型由模板参数决定,而难以表示它的问题。
auto在这里的作用也称为返回值占位,它只是为函数返回值占了一个位置,真正的返回值是后面的decltype(_Tx*_Ty)。为何要将返回值后置呢?如果没有后置,则函数声明时为:

decltype(x*y)multiply(_Tx x, _Ty y)

而此时x,y还没声明呢,编译无法通过。

4.4 auto注意事项

  • auto 变量必须在定义时初始化,这类似于const关键字。
  • 定义在一个auto序列的变量必须始终推导成同一类型。例如:

    auto a4 = 10, a5 = 20, a6 = 30;//正确
    auto b4 = 10, b5 = 20.0, b6 = 'a';//错误,没有推导为同一类型
    使用auto关键字做类型自动推导时,依次施加一下规则:
  • 如果初始化表达式是引用,则去除引用语义。

    int a = 10;
    int &b = a;
    
    auto c = b;//c的类型为int而非int&(去除引用)
    auto &d = b;//此时c的类型才为int&
    
    c = 100;//a =10;
    d = 100;//a =100;
  • 如果初始化表达式为const或volatile(或者两者兼有),则除去const/volatile语义。

    const int a1 = 10;
    auto  b1= a1; //b1的类型为int而非const int(去除const)
    const auto c1 = a1;//此时c1的类型为const int
    b1 = 100;//合法
    c1 = 100;//非法
  • 如果auto关键字带上&号,则不去除const语意。

    const int a2 = 10;
    auto &b2 = a2;//因为auto带上&,故不去除const,b2类型为const int
    b2 = 10; //非法
    这是因为如何去掉了const,则b2为a2的非const引用,通过b2可以改变a2的值,则显然是不合理的。
  • 初始化表达式为数组时,auto关键字推导类型为指针。

    int a3[3] = { 1, 2, 3 };
    auto b3 = a3;
    cout << typeid(b3).name() << endl;

    程序将输出

    int *

  • 若表达式为数组且auto带上&,则推导类型为数组类型。

    int a7[3] = { 1, 2, 3 };
    auto & b7 = a7;
    cout << typeid(b7).name() << endl;

    程序输出

    int [3]

  • 函数或者模板参数不能被声明为auto

    void func(auto a)  //错误
    {
    //... 
    }
  • 时刻要注意auto并不是一个真正的类型。
    auto仅仅是一个占位符,它并不是一个真正的类型,不能使用一些以类型为操作数的操作符,如sizeof或者typeid。

    cout << sizeof(auto) << endl;//错误
    cout << typeid(auto).name() << endl;//错误

4.5 个人应用

在vector名为tmp中,如何遍历?这种看上去更像是python的遍历。(python中很多简便的语法是从c++中引申过来的)

直接 for(auto it : tmp)即可表示it遍历了tmp中的所有元素,非常简便。也可以for( int it:tmp)更简便,也可以。这点语法更类似于用it表示tmp中元素的个数,然后完成循环。循环的因子是int。此应用必须熟练掌握,非常简便。

	vector<int> tmp;
	tmp.push_back(n);

	for (int i = n / 2; i >= 1; i--){
		if (n%i == 0 && m%i == 0)tmp.push_back(i);
	}

	for (auto it : tmp){
		string ss = str_2.substr(0, it);
		string A, B;
		for (int k = 0; k<n / it; k++){
			A += ss;
		}
		for (int k = 0; k<m / it; k++){
			B += ss;
		}
		if (A == str_2 && B == str_1){
			cout << ss; break;
		}
	}
发布了210 篇原创文章 · 获赞 584 · 访问量 30万+

猜你喜欢

转载自blog.csdn.net/weixin_36474809/article/details/100053957