C++11-列表初始化/变量类型推导/范围for/final&override/默认成员函数控制

  • 零、前言
  • 一、C++11简介
  • 二、列表初始化
    • 1、内置类型列表初始化
    • 2、自定义类型列表初始化
  • 三、变量类型推导
    • 1、auto类型推导
    • 2、decltype类型推导
  • 四、范围for循环
  • 五、final和override
    • 1、final
    • 2、override
  • 六、默认成员函数控制

零、前言

本章将开始学习C++11的新语法特性,主要是一些比较常用的语法

一、C++11简介

  • 发展历程:
  1. 在2003年C++标准委员会曾经提交了一份技术勘误表(简称TC1),使得C++03这个名字已经取代了C++98称为C++11之前的最新C++标准名

  2. 不过由于TC1主要是对C++98标准中的漏洞进行修复,语言的核心部分则没有改动,因此人们习惯性的把两个标准合并称为C++98/03标准

  3. 从C++0x到C++11,C++标准10年磨一剑,第二个真正意义上的标准珊珊来迟。相比于C++98/03,C++11则带来了数量可观的变化,其中包含了约140个新特性,以及对C++03标准中约600个缺陷的修正,这使得C++11更像是从C++98/03中孕育出的一种新语言。

  4. 相比较而言,C++11能更好地用于系统开发和库开发、语法更加泛华和简单化、更加稳定和安全,不仅功能更强大,而且能提升程序员的开发效率

二、列表初始化

  • 背景引入:

在C++98中,标准允许使用花括号{}对数组元素进行统一的列表初始值设定

  • 示例:
int array1[] = {1,2,3,4,5};
int array2[5] = {0};

注:对于一些自定义的类型,却无法使用这样的初始化

1、内置类型列表初始化

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

  • 示例:
int main()
{// 内置类型变量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};map<int, int> m{{1,1}, {2,2,},{3,3},{4,4}};return 0;
}
  • 效果:

注:列表初始化可以在{}之前使用等号,其效果与不使用=没有什么区别

2、自定义类型列表初始化

  1. 标准库支持单个对象的列表初始化
class Pointer
{public:Pointer(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;
}
  1. 多个对象的列表初始化

多个对象想要支持列表初始化,需要实现initializer_list类型参数的构造函数

  • 示例:
#include <initializer_list>
template<class T>
class Vector {public:// ...Vector() : _capacity(0), _size(0){}Vector(initializer_list<T> l) : _capacity(l.size()), _size(0){_array = new T[_capacity];for (auto e : l)_array[_size++] = e;}Vector<T>& operator=(initializer_list<T> l) {delete[] _array;size_t i = 0;for (auto e : l)_array[i++] = e;return *this;}// ...
private:T* _array;size_t _capacity;size_t _size;
};
int main()
{Vector<int> v1{ 1,2,3,4 };Vector<int> v2;v2 = { 1,2,3,4,5 };return 0;
}
  • 效果:

注:initializer_list是系统自定义的类模板,该类模板中主要有三个方法:begin()**、**end()迭代器以及获取区间中元素个数的方法size()

三、变量类型推导

1、auto类型推导

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

  • 示例:
void test1()
{short a = 32670;short b = 32670;// c如果给成short,会造成数据丢失,如果能够让编译器根据a+b的结果推导c的实际类型,就不会存在问题short c = a + b;cout<<c<<endl;
}
void test2()
{std::map<std::string, std::string> m{{"apple", "苹果"}, {"banana","香蕉"}};// 使用迭代器遍历容器, 迭代器类型太繁琐std::map<std::string, std::string>::iterator it = m.begin();while(it != m.end()){cout<<it->first<<" "<<it->second<<endl;++it;}
}

C++11中,可以使用auto来根据变量初始化表达式类型推导变量的实际类型,可以给程序的书写提供许多方便。将程序中c与it的类型换成auto,程序可以通过编译,而且更加简洁

  • 示例:
void test3()
{short a = 32670;short b = 32670;// c如果给成short,会造成数据丢失,如果能够让编译器根据a+b的结果推导c的实际类型,就不会存在问题auto c = a + b;cout<<c<<endl;std::map<std::string, std::string> m{{"apple", "苹果"}, {"banana","香蕉"}};// 使用迭代器遍历容器, 迭代器类型太繁琐auto it = m.begin();while(it != m.end()){cout<<it->first<<" "<<it->second<<endl;++it;}
}
  • 效果:

2、decltype类型推导

  • 为什么需要decltype:
  1. auto使用的前提是:必须要对auto声明的类型进行初始化,否则编译器无法推导出auto的实际类型
  2. 但有时候可能需要根据表达式运行完成之后结果的类型进行推导,因为编译期间,代码不会运行,此时auto也就无能为力
  • 示例:
template<class T1, class T2>
T1 Add(const T1& left, const T2& right)
{return left + right;
}

注:如果能用加完之后结果的实际类型作为函数的返回值类型就不会出错,但这需要程序运行完才能知道结果的实际类型,即RTTI(Run-Time Type Identification 运行时类型识别)

  • C++98中确实已经支持RTTI:
  1. typeid只能查看类型不能用其结果类定义类型
  2. dynamic_cast只能应用于含有虚函数的继承体系中

注:运行时类型识别的缺陷是降低程序运行的效率

  • decltype的使用:

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

  1. 推演表达式类型作为变量的定义类型
  • 示例:
int main()
{int a = 10000000000000;int b = 10000000000000;// 用decltype推演a+b的实际类型,作为定义c的类型decltype(a + b) c;cout<<typeid(c).name()<<endl;return 0;
}
  1. 推演函数返回值的类型
  • 示例:
void* GetMemory(size_t size)
{return malloc(size);
}
int main()
{// 如果没有带参数,推导函数的类型cout << typeid(decltype(GetMemory)).name() << endl;// 如果带参数列表,推导的是函数返回值的类型,注意:此处只是推演,不会执行函数cout << typeid(decltype(GetMemory(0))).name() << endl;return 0;
}
  • 效果:

四、范围for循环

在 C++98/03 中,不同的容器和数组遍历的方式不尽相同,写法不统一,也不够简洁,而 C++11 基于范围的 for 循环可以以简洁、统一的方式来遍历容器和数组,用起来也更方便了

  • 示例:
int main(void)
{vector<int> v = { 1, 2, 3, 4, 5, 6 };for (auto it = v.begin(); it != v.end(); ++it){cout << *it << " ";}cout << endl;for (auto& value : v){cout << value << " ";}cout << endl;return 0;
}
  • 效果:

  • C++11基于范围的for循环语法格式:
for (declaration : expression)
{// 循环体
}
  • 解释:

declaration 表示遍历声明,在遍历过程中,当前被遍历到的元素会被存储到声明的变量中。expression 是要遍历的对象,它可以是 表达式容器数组初始化列表

五、final和override

1、final

C++ 中增加了 final 关键字来限制某个类不能被继承,或者某个虚函数不能被重写。如果使用 final 修饰函数,只能修饰虚函数,并且要把final关键字放到类或者函数的后面

  1. 修饰函数:

如果使用 final 修饰函数,只能修饰虚函数,这样就能阻止子类重写父类的这个函数了:

class Base
{public:virtual void test() final{cout << "Base class..."<<'\n';}virtual void test2(){cout << "Base class test2..."<<'\n';}
};
class Child : public Base
{public://带有final, 无法重写void test() {cout << "Child class..." << '\n';}void test2(){cout << "Child class test2..." << '\n';}
};
  • 效果:

  1. 修饰类

使用 final 关键字修饰过的类是不允许被继承的,也就是说这个类不能有派生类

  • 示例:
class Base final
{public:virtual void test(){cout << "Base class..." << '\n';}virtual void test2(){cout << "Base class test2..." << '\n';}
};class Child : public Base
{public:void test() {cout << "Child class..." << '\n';}void test2(){cout << "Child class test2..." << '\n';}
};
  • 效果:

2、override

override 关键字确保在派生类中声明的重写函数与基类的虚函数有相同的签名,同时也明确表明将会重写基类的虚函数。这样就可以保证重写的虚函数的正确性,也提高了代码的可读性,和 final 一样这个关键字要写到方法的后面

  • 示例:
class Base
{public:void test(){cout << "Base class...";}
};
class Child : public Base
{public:void test() override{cout << "Child class...";}
};
  • 效果:

注:使用了 override 关键字之后,假设在重写过程中因为误操作,写错了函数名或者函数参数或者返回值编译器都会提示语法错误

六、默认成员函数控制

  • 引入背景:
  1. 在C++中对于空类编译器会生成一些默认的成员函数,如果在类中显式定义了,编译器将不会重新生成默认版本
  2. 有时候这样的规则可能被忘记,最常见的是声明了带参数的构造函数,必要时则需要定义不带参数的版本以实例化无参的对象。而且有时编译器会生成,有时又不生成,容易造成混乱,于是C++11让程序员可以控制是否需要编译器生成
  1. 显式缺省函数

在C++11中,可以在默认函数定义或者声明时加上=default,从而显式的指示编译器生成该函数的默认版本,用=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;
int main()
{A a1(10);A a2;a2 = a1;return 0;
}
  1. 删除默认函数
  1. 如果能想要限制某些默认函数的生成,在C++98中,是该函数设置成private,并且不给定义,这样只要其他人想要调用就会报错
  2. 在C++11中更简单,只需在该函数声明加上=delete即可,该语法指示编译器不生成对应函数的默认版本,称=delete修饰的函数为删除函数
  • 示例:
class A
{public:A(int a) : _a(a){}// 禁止编译器生成默认的拷贝构造函数以及赋值运算符重载A(const A&) = delete;A& operator=(const A&) = delete;
private:int _a;
};
int main()
{A a1(10);// 编译失败,因为该类没有拷贝构造函数A a2(a1);// 编译失败,因为该类没有赋值运算符重载A a3(20);a3 = a2;return 0;
}

C++11-列表初始化/变量类型推导/范围for/finaloverride/默认成员函数控制相关推荐

  1. 【初阶与进阶C++详解】第二十二篇:C++11新特性(列表初始化+变量类型推到+右值引用+新增默认成员函数+可变模板参数+lambda表达式+包装器function_bind)

  2. 设计一个长方形类。成员变量包括:长度和宽度,成员函数除包括计算周长和计算面积外, 还包括用set方法来设置长方形的长度和宽度,以及用get的方法来获得长方形的长度和宽度 最后,编写一个测试程序来测试所

    本文为博主原创文章,未经博主允许不得转载. 版权为陈博超所有,第一次于2020年11月22日发表于BLOG上 本BLOG上原创文章未经本人许可,不得用于商业用途.转载请经允许后注明出处,否则保留追究法 ...

  3. C++11列表初始化

    列表初始化: 1.旧语法中定义了初始化的几种不同形式,如下: int data = 0; //赋值初始化 int data = {0}; //花括号初始化 int data(0); //构造初始化 i ...

  4. C++11 列表初始化(list_initialization)

    一 列表初始化 定义:从花括号初始化器列表初始化对象. 语法: // 直接列表初始化 T object { arg1, arg2, ... }; (1) T { arg1, arg2, ... } ( ...

  5. C++11并发与多线程笔记(10) future其他成员函数、shared_future、atomic

    第十节 future其他成员函数.shared_future.atomic 一.std::future 的成员函数 1.std::future_status status = result.wait_ ...

  6. C++11 运行时变量类型判断

    #include <typeinfo> string name = "cpp"; int age = 14; cout << typeid(age).nam ...

  7. C++11 类型推导decltype

    我们之前使用的typeid运算符来查询一个变量的类型,这种类型查询在运行时进行.RTTI机制为每一个类型产生一个type_info类型的数据,而typeid查询返回的变量相应type_info数据,通 ...

  8. C++11 就地初始化与列表初始化

    文章目录 1.就地初始化 1.1 简介 1.2 就地初始化与初始化列表的先后顺序 2.列表初始化 参考文献 1.就地初始化 1.1 简介 在 C++11 之前,只能对结构体或类的静态常量成员就地初始化 ...

  9. C++11 变量类型获取typeid

    文章目录 1.typeid使用 1) 原型:const char* name() const; 2) 原型:bool before (const type_info& rhs) const; ...

最新文章

  1. 的电路接法_放大电路的三种基本接法分享
  2. 什么是反射?反射的用法?实例说明。
  3. ustc小道消息20211227
  4. 有子对象的派生类的构造函数
  5. Python-OpenCV基本操作cv2
  6. Sublime text3 新建 HTML文件
  7. linux中使用yum的优点,linux – 自动“yum update”以保证服务器安全 – 优点和缺点?...
  8. ios开发之.pch文件的使用
  9. 连接oracle报错:Invalid connection string format, a valid format is: host:port:sid
  10. 开发者如何区分 5G 和 LTE 技术?
  11. 一片文章带你理解再生核希尔伯特空间(RKHS)以及各种空间
  12. opencv查看版本路径
  13. 高端android手机,7月Android中高端手机性能榜出炉:华为高端落榜,中端没进前三!...
  14. [单片机框架][bsp层][AT32F415][bsp_adc] adc配置和使用
  15. 羊城杯2022--Writeup
  16. 网易数据湖探索与实践-范欣欣
  17. Python将excel或者csv表格中的空行删除
  18. 小米电视微信投屏出现服务器出错,同一wifi下无法投屏怎么办 小米电视不能投屏的解决方法...
  19. 如何使用Flutter Widget构建响应式应用程序评论
  20. javascript 设计模式

热门文章

  1. 小明的字符串--牛客
  2. 物理层设备(中继器和集线器)
  3. linux用不了wifi密码忘记了,极路由wifi密码忘记了怎么办?
  4. 《机器学习》(周志华)线性回归
  5. FIN_WAIT_2的超时时间
  6. CISCO MFC中部署Firepower FTD高可用(HA)---By 年糕泰迪
  7. 兔子与狐狸c语言,【狐狸和兔子的故事】_ 狐狸和兔子故事_亲亲宝贝网
  8. 转:一套大而全的系统架构体系与具体落地方案
  9. python中string和bool的转换
  10. AAAI2021论文列表