1.列表初始化
1.1扩展了初始化列表(用{}括起来的列表)的使用范围
使其可用于所有的内置类型和用户自定义的类型,使用初始化列表可以加=(没区别),也可不加。例如以下用法:
int a = { 1 }; //多此一举int b{ 2 };int c{ 1 + 2 };int d{ a + b };vector v = { 1, 2, 3, 4 };vectorvs = { "string", "vector" };int *arr = new int[]{ 1, 2, 3, 4};map mymap{ { 1, 1 }, { 2, 2 } };
1.2自定义类型的列表初始化
class Date{public: Date(int year, int month, int day) :_year(year) , _month(month) , _day(day) {}private: int _year; int _month; int _day;};int main(){ Date d1{ 2019, 6, 26 }; //标准库支持单个对象使用列表进行初始化 return 0;}
对于多个对象的列表初始化:
首先认识一个新的数据结构 initializer_list
#includeint main(){ initializer_list initlist{ 1, 2, 3, 4 }; auto it = initlist.begin(); for (; it != initlist.end(); ++it) { cout << *it << " "; } cout << endl; cout << initlist.size() << endl; return 0;}
initializer_list支持迭代器,size()接口
在多个对象的列表初始化时,C++11是用了initializer_list,必须包含一个带有initializer_list参数的构造函数,类似:
templateclass Vector{public: Vector(initializer_list l) //包含一个带有initializer_list参数的构造函数 :_capacity(l.size()) , _size(0) { _arr = new T[_capacity]; for (const auto& it : l) { _arr[_size++] = it; } } size_t size() { return _size; } T& operator[](size_t i) { return *(_arr + i); }private: T *_arr; size_t _size; size_t _capacity;};int main(){ Vector V{ 1, 2, 3, 4 }; //用{}内容构建了一个initializer_list对象 for (size_t i = 0; i < V.size(); ++i) { cout << V[i] << " "; } cout << endl; return 0;}
2.变量类型推导
2.1 auto
当变量类型写起太复杂或不知道实际类型时,例如:
int main(){ short a = 32760; short b = 32765; //若指定c的类型为short,则会造成数据丢失, //使用auto,让编译器自己推演c的实际类型,就不会存在问题 auto c = a + b; //c的类型为int unordered_mapmymap{ { 1, 1 }, { 2, 2 } }; //unordered_map ::iterator it = mymap.begin(); auto it = mymap.begin(); //使用auto可避免如上复杂的写法 return 0;}
2.2 decltype类型推导
auto在使用时必须对变量进行初始化,编译器才能推演出变量的实际类型。
使用decltype可根据表达式的实际类型推演出定义变量时所用的类型,例如:
根据类型推导定义变量:
int a = 1;int b = 2;decltype(a + b) c; //推演为int c;
推演函数返回值类型:
void *func(){ return (void *)0;}int main(){ //不带参数列表是,返回函数的类型 cout << typeid(decltype(func)).name() << endl; //void * __cdecl(void) //带参数列表时,返回函数返回值类型 cout << typeid(decltype(func())).name() << endl; //void * return 0;}
decltype使用RTTI(运行时类型识别),C++98也支持了RTTI,在typeid和dynamic_cast中有使用。运行时类型识别会降低程序运行效率。
3.基于范围for的循环
vector v{ 1, 2, 3, 4 };for (auto it : v){ //...}
4.final和override
用final修饰的类不可被继承:
class A final{public: A(int data = 1) :_data(data) {} void print() { cout << _data << endl; }private: int _data;};class B : public A //不能将“final”类类型用作基类{};
用final修饰的虚函数不可被重写:
class A{public: A(int data = 1) :_data(data) {} virtual void print() final { cout << _data << endl; }private: int _data;};class B : public A{ void print() //无法重写“final”函数 "A::print" { //... }};
override关键字表示当前函数重写了基类中的虚函数:
class A{public: A(int data = 1) :_data(data) {} virtual void print() { cout << _data << endl; }private: int _data;};class B : public A{public: virtual void print() override;};int main(){ B b; b.print(); return 0;}
被override修饰的函数必须重写基类中该虚函数,不会继承基类中该函数的实现,以上程序链接失败,因为print函数在派生类中被override修饰,却没有具体实现。
5.委派构造函数
委派函数将构造的任务委派给目标构造函数来完成的一种类构造的方式。
class Info{public: //目标构造函数:将构造函数体中重复的代码提出来作为一个基础版本,用于在其它构造函数中调用 Info() //被委派 :_a(1) , _c('c') { Init();//一些初始化行为 } //委派构造函数 Info(int a) :Info() //委派 { _a = a; } Info(char c) :Info() //委派 { _c = c; }private: void Init() { //... }private: int _a; char _c;};
构造函数不能同时“委派”和使用参数列表。
6.默认函数控制
在C++中对一个空类编译器会生成一些默认成员函数,例如构造函数、拷贝构造、赋值运算符重载、析构函数、&和const&的重载、移动构造、移动拷贝构造等。如果显示的定义了,则不会生成默认的。而有时又需要一个无参的构造函数,C++11可以显示的控制是否生成默认的函数。
6.1生成默认函数
C++11中在默认函数声明或定义后加=default,表示让编译器生成该函数的默认版本(显示缺省函数)。
class A{public: A(int a) :_a(a) {} A() = default; //声明时指定生成默认版本 A& operator=(const A& a); //声明private: int _a;};A& A::operator=(const A& a) = default; //定义时让编译器生成默认版本
6.2删除默认函数
如果想要限制某些默认函数的生成,在C++98中我们会将该函数声明为私有不实现。在C++11中我们只需在函数声明后加上=delete即可,编译器就不会生成该函数。
class A{public: A(int a) :_a(a) {} A() = default; A& operator=(const A& a) = delete;private: int _a;};int main(){ A a(1); A b; b = a; //无法引用 函数 "A::operator=(const A &a)"它是已删除的函数 return 0;}