目录
一、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++语言更加现代化、高效和易用。一些重要的特性包括:
自动类型推导(auto关键字):允许编译器根据初始化表达式的类型推导变量的类型,简化代码书写。
Lambda表达式:允许在函数内部定义匿名函数,提高代码的可读性和灵活性。
移动语义(右值引用和移动语义):引入了右值引用和移动语义,提高了程序的性能和效率。
列表初始化:引入了统一的初始化语法,使得初始化更加简洁和一致。
强类型枚举:引入了枚举类(enum class),提供了更好的类型安全性。
多线程支持(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
在定义变量时,必须先给出变量的实际类型,编译器才允许定义,但有些情况下可能不知道需要实际类型怎么给,或者类型写起来特别复杂
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;
};