C++11---(1)

时间:2024-02-17 14:28:57

目录

一、C++11简介

二、列表初始化

2.1、{ } 初始化

三、变量类型推导

3.1、auto

3.2、decltype

为什么需要decltype

四、final和override

4.1、final

4.2、override

 五、默认成员函数控制

5.1、default修饰函数

5.2、delete修饰函数

六、nullptr


一、C++11简介

C++11是C++语言的一个重要版本,于2011年发布。它引入了许多新的特性和改进,使得C++语言更加现代化、高效和易用。一些重要的特性包括:

  1. 自动类型推导(auto关键字):允许编译器根据初始化表达式的类型推导变量的类型,简化代码书写。

  2. Lambda表达式:允许在函数内部定义匿名函数,提高代码的可读性和灵活性。

  3. 移动语义(右值引用和移动语义):引入了右值引用和移动语义,提高了程序的性能和效率。

  4. 列表初始化:引入了统一的初始化语法,使得初始化更加简洁和一致。

  5. 强类型枚举:引入了枚举类(enum class),提供了更好的类型安全性。

  6. 多线程支持(std::thread、std::mutex等):标准库中引入了多线程支持,使得并发编程更加容易。

总的来说,C++11使得C++语言更加现代化和强大,为程序员提供了更多的工具和特性来编写高效、可维护的代码

二、列表初始化

2.1、{ } 初始化

C++98中,标准允许使用花括号"{}"来对数组结构体进行统一的列表初始化:             

                                    

C++98对于自定义类型,无法使用列表初始化,在C++11中改进了

C++11中自定义类型也可以使用列表初始化
C++11扩大了用大括号括起的列表(初始化列表)的使用范围,使其可用于所有的内置类型和用户自定义的类型,使用初始化列表时,可添加等号(=),也可不添加

这也就是说,在C++11后,不止数组和结构体可以用{}初始化,变量也可以了。并且可以不使用赋值符号:

                                   

内置类型初始化

// 内置类型变量
int x1 = {10};
int x2{10};//建议使用原来的
int x3 = 1+2;
int x4 = {1+2};
int x5{1+2};
// 数组
int arr1[5] {1,2,3,4,5};
int arr2[]{1,2,3,4,5};
// 动态数组,在C++98中不支持
int* arr3 = new int[5]{1,2,3,4,5};
// 标准容器
vector<int> v{1,2,3,4,5};//这种初始化就很友好,不用push_back一个一个插入
map<int, int> m{{1,1}, {2,2,},{3,3},{4,4}};

自定义类型的列表初始化

1、单个列表初始化

class Point
{
public:
	Point(int x = 0, int y = 0): _x(x), _y(y)
{}
private:
	int _x;
	int _y;
};
int main()
{
	Pointer p = { 1, 2 };
	Pointer p{ 1, 2 };//不建议
return 0;
}

2、多个对象的列表初始化
多个对象想要支持列表初始化,需给该类(模板类)添加一个带有initializer_list类型参数的构造函数即可。
注意:initializer_list是系统自定义的类模板,该类模板中主要有三个方法:begin()、end()迭代器以及获取区间中元素个数的方法size()

class Date
{
public:
	Date(int year = 0, int month = 1, int day = 1)
		:_year(year)
		, _month(month)
		, _day(day)
	{
		cout << "这是日期类" << endl;
	}

private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	//C++11容器都实现了带有initializer_list类型参数的构造函数
	vector<Date> vd = { { 2022, 1, 17 }, Date{ 2022, 1, 17 }, { 2022, 1, 17 } };
	return 0;
}

三、变量类型推导


3.1、auto

在定义变量时,必须先给出变量的实际类型,编译器才允许定义,但有些情况下可能不知道需要实际类型怎么给,或者类型写起来特别复杂

C++11 auto 用于实现自动类型推断。这样要求必须进行显示初始化,让编译器将定义对象的类型设置为初 始化值的类型。
int main()
{
    int i = 10;
    auto p = &i;
    auto pf = strcpy;
    cout << typeid(p).name() << endl;
    cout << typeid(pf).name() << endl;
    map<string, string> dict = { {"sort", "排序"}, {"insert", "插入"} };
    //map<string, string>::iterator it = dict.begin();
    auto it = dict.begin();
    return 0;
}

3.2、decltype

为什么需要decltype

auto使用的前提是:必须要对auto声明的类型进行初始化,否则编译器无法推导出auto的实际类型。但有时候可能需要根据表达式运行完成之后结果的类型进行推导,因为编译期间,代码不会运行,此时auto也就无能为力。

decltype是根据表达式的实际类型推演出定义变量时所用的类型,比如

1、推演表达式类型作为变量的定义类型

int a = 10, b = 20;
decltype(a + b)c;
cout << typeid(c).name() << endl;
//输出结果是int

2. 推演函数返回值的类型

template<class T1, class T2>
T1 Add(const T1& left, const T2& right)
{
	return left + right;
}
int main()
{
	cout << typeid(Add(1, 2)).name() << endl;
	return 0;
}
//输出结果是int

四、final和override

4.1、final

1、final修饰类的时候,表示该类不能被继承

class A final //表示该类是最后一个类
{
private:
	int _year;
};
class B : public A //无法继承,报错
{

};

2、final修饰虚函数时,这个虚函数不能被重写

class A 
{
public:
	virtual void fun() final//修饰虚函数
	{
		cout << "this is A" << endl;
	}
private:
	int _year;
};
class B : public A
{
public:
	virtual void fun()//父类虚函数用final修饰,表示最后一个虚函数,无法重写
	{
		cout << "this is B" << endl;
	}
};

4.2、override

检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错

class A 
{
public:
	virtual void fun()
	{
		cout << "this is A" << endl;
	}
private:
	int _year;
};
class B : public A
{
public:
	virtual void fun1() override  //报错,“使用override声名的成员函数不能重写基类成员”
	{
		cout << "this is B" << endl;
	}
};

 五、默认成员函数控制

在C++中对于空类编译器会生成一些默认的成员函数,比如:构造函数、拷贝构造函数、运算符重载、析构函数和&和const&的重载、移动构造、移动拷贝构造等函数。如果在类中显式定义了,编译器将不会重新生成默认版本。有时候这样的规则可能被忘记,最常见的是声明了带参数的构造函数,必时则需要定义不带参数的版本以实例化无参的对象。而且有时编译器会生成,有时又不生成,容易造成混乱,于是C++11让程序员可以控制是否需要编译器生成。

5.1、default修饰函数

在C++11中,可以在默认函数定义或者声明时加上=default,从而显式的指定编译器生成该函数的默认版本(默认成员函数),用=default修饰的函数称为显式缺省函数

class A 
{
public:
	A() = default;//让编译器默认生成无参构造函数
	A(int year)    //这样不写缺省值的时候,就不需要自己在去实现一个默认的无参构造函数
		:_year(year)
	{}
	void fun()
	{
		cout << "this is A" << endl;
	}
private:
	int _year;
};

5.2、delete修饰函数

如果能想要限制某些默认函数的生成,在C++98中,是该函数设置private,并且不给定义,这样只要其他人想要调用就会报错。在C++11中更简单,只需在该函数声明加上=delete即可,该语法指示编译器不生成对应函数的默认版本,称=delete修饰的函数为删除函数

class A
{
public:
	A() = default;
	A(int a) : _a(a)
	{}
	//C++11
	// 禁止编译器生成默认的拷贝构造函数以及赋值运算符重载
	A(const A&) = delete;
	A& operator=(const A&) = delete;
private:
	int _a;
	//C++98,设置成private就可以了
	A(const A&) = delete;
	A& operator=(const A&) = delete;
};

六、nullptr

由于 C++ NULL 被定义成字面量 0 ,这样就可能回带来一些问题,因为 0 既能指针常量,又能表示
整形常量。所以出于清晰和安全的角度考虑, C++11 中新增了 nullptr ,用于表示空指针。