C++11新特性精讲(多线程除外)
Posted 森明帮大于黑虎帮
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++11新特性精讲(多线程除外)相关的知识,希望对你有一定的参考价值。
文章目录
1、什么是 C+11
-
C++11 标准为 C++编程语言的第三个官方标准, 正式名叫 ISO/IEC 14882:2011 - Information technology -Programming languages – C++。 在正式标准发布前, 原名 C++0x。 它将取代 C++标准第二版 ISO/IEC 14882:2003 - Programming languages – C++成为 C++语言新标准。
-
C++11 是对目前 C++语言的扩展和修正, C++11 不仅包含核心语言的新机能, 而且扩展了
C++的标准程序库(STL) , 并入了大部分的 C++ Technical Report 1(TR1) 程序库(数学
的特殊函数除外)。 -
C++11 包括大量的新特性: 包括 lambda 表达式, 类型推导关键字 auto、 decltype, 和模板
的大量改进。
1、Qt Creator 新建一个纯C++的项目,在项目文件配置:CONFIG += c++11
2、终端,g++ xxx.cpp -std=c++11
2、类型推导
2.1 auto的使用
- auto的自动类型推导,用于初始化表达式中推断出变量的数据类型。从这个意义上讲,auto并非一种"类型"声明,而是一个类型声明时的"占位符",编译器在编译时期会将auto替换为变量实际的类型。
- 通过auto的自动类型推导,可以大大简化我们的编程工作。
在C中,auto修饰局部变量,局部变量也叫auto变量,自动变量
int a;
auto int a;
C++11, auto根据用户初始化内容自动推导类型
auto使用代码
#include <iostream>
#include<vector>
double foo()
//...
//自定义类型:结构体、枚举、联合体
//struct是声明结构体类型时所必须使用的关键字
//Test是结构体名字
struct Test
int a;
;
void func(std::vector<std::string>& tmp)
//自动推导i的类型
for(auto i=tmp.begin();i<tmp.end();i++)
//...
int main()
/*
在C中,auto修饰局部变量,局部变量也叫auto变量,自动变量
int a; 等价于auto int a;
auto int a;
C++ 11, auto根据用户初始化内容自动推导类型
*/
//内置类型
int a=1;
auto b=1; //的类型就是int
auto x = 1; // x的类型为int
auto y = foo(); // y的类型是double
//自定义类型
struct Test str=0; //结构体 = 0; 这种初始化方式来对结构体进行初始化,是可行的。
auto d=str; //d的类型就是struct Test类型
return 0;
2.2 auto的易错点
#include<iostream>
#include<vector>
//vs2013不支持,函数形参是auto变量,qt可以
void func(auto a)
//.....
//3.auto 变量不能作为自定义类型的成员变量
struct test
int a;
auto b=10;
;
int main()
//1.定义变量时,必须初始化
auto a;
a=10;
//4.不能是auto数组
auto b[3]=1,2,3;
//5.模板实例化类型不能是auto类型
std::vector<int> a;
std::vector<auto>b=1;
system("pause");
return 0;
2.3 decltype
decltype 实际上有点像 auto 的反函数, auto 可以让你声明一个变量, 而 decltype 则可以从一个变量或表达式中得到其类型, 如下:
#include<iostream>
#include<typeinfo> //typeid
#include<vector>
int main()
//decltype 实际上有点像 auto 的反函数, auto 可以让你声明一个变量, 而 decltype 则可以从一个变量或表达式中得到其类型, 如下:
int i;
decltype(i) j=0;
std::cout<<typeid(j).name()<<std::endl;
float a;
decltype(a) b;
std::cout<<typeid(b).name()<<std::endl;
double c;
decltype(a+c) d;
std::cout<<typeid(d).name()<<std::endl;
std::vector<int> vec;
decltype(vec.begin()) tmp;
for(tmp=vec.begin();tmp!=vec.end();tmp++)
//...
//匿名类型的枚举变量
enum
OK,
ERROR
flag;
decltype(flag) flag2;
return 0;
2.4 追踪返回类型
返回类型后置: 在函数名和参数列表后面指定返回类型。
#include<iostream>
int Add(int a,int b)
return a+b;
auto Sub(int a,int b) -> int
return a-b;
auto Div(int a,double b) -> decltype(a/b)
return a/b;
template<class T1,class T2>
auto Mul(const T1& t1,const T2& t2) -> decltype(t1*t2)
return t1*t2;
int main()
auto a=10;
auto b=20;
std::cout<<Add(a,b)<<std::endl;
std::cout<<Sub(a,b)<<std::endl;
auto c=30;
auto d=5.5;
std::cout<<Div(c,d)<<std::endl;
std::cout<<Mul(c,d)<<std::endl;
return 0;
3、易用性的改进
3.1 初始化
3.1.1 类中内成员变量初始化
#include<iostream>
#include<string>
class A
public:
A(int a)
:a_(a) //初始化列表给a_初始化
//a_=a
int a_;
;
class B
public:
int data11; // 使用"="初始化非静态普通成员, 也可以 int data1;
int data2=2;
A tmp10; // 对象成员, 创建对象时, 可以使用来调用构造函数
std::string name"c++";
;
int main()
B b;
std::cout<<b.data1<<std::endl;
std::cout<<b.data2<<std::endl;
std::cout<<b.tmp.a_<<std::endl;
std::cout<<b.name<<std::endl;
return 0;
3.1.2 列表初始化
C++11 引入了一个新的初始化方式, 称为初始化列表(List Initialize), 具体的初始化方式如下:
初始化列表可以用于初始化结构体类型, 例如:
#include<iostream>
using namespace std;
struct Test
int a;
int b;
char name[50];
;
int main(void)
struct Test test=1,2,"mike"; //列表初始化
int a=1;
int b=1; //ok,列表初始化
int c2; //ok
int arr1[]=1,2,3;
int arr2[]1,2,3;
return 0;
其他一些不方便初始化的地方使用, 比如 std的初始化, 如果不使用这种方式, 只能
用构造函数来初始化, 难以达到效果 。
std::vector<int> vec1(3,5);
std::vector<int> vec2=5,5,5;
std::vector<int> vec3=1,2,3,4,5; //不使用列表初始化用构造函数难以实现
3.1.3 防止类型收缩
类型收窄指的是导致数据内容发生变化或者精度丢失的隐式类型转换。 使用列表初始化可以防止类型收窄。
#include<iostream>
int main()
int a=512;
char b=a; //char 256位
std::cout<<"b= "<<std::endl;
return 0;
输出结果:
b=
#include<iostream>
int main()
int a=512;
char b=a;
std::cout<<"b= "<<std::endl;
return 0;
编译不通过。
PS D:\\C++11代码\\build> cmake --build .
用于 .NET Framework 的 Microsoft (R) 生成引擎版本 16.7.0+b89cb5fde
版权所有(C) Microsoft Corporation。保留所有权利。
列表初始化防止类型收缩.cpp
D:\\C++11代码\\易用性的改进\\初始化\\类中成员变量直接初始化\\列表初始化防止类型收缩.cpp(5,14): error C2397: 从“int”转换到“char”需要收缩转换 [D:\\C++11代码\\build\\易用性的改进\\初始化\\类中成员变量直接初始化\\main.vcxproj]
PS D:\\C++11代码\\build>
#include <iostream>
int main(void)
int a = 512;
char b = a;
std::cout << "b= " << std::endl;
const int x = 1024;
const int y = 10;
char a = x; // 收窄, 但可以通过编译
char *b = new char(1024); // 收窄, 但可以通过编译
char c = x; // err, 收窄, 无法通过编译
char d = y; // 可以通过编译
unsigned char e-1; // err,收窄, 无法通过编译
float f7; // 可以通过编译
int g2.0f; // err,收窄, 无法通过编译
float *h = new float1e48; // err,收窄, 无法通过编译
float i = 1.2l; // 可以通过编译
return 0;
3.2 基于范围的for循环
在 C++中 for 循环可以使用基于范围的 for 循环, 示例代码如下:
#include<iostream>
int main()
int arr[]=1,2,3,4,5,6,7,8,9,10;
int n=sizeof(arr)/sizeof(*arr); //元素个数
std::cout<<n<<std::endl;
for(int i=0;i<n;i++)
int tmp=arr[i];
std::cout<<tmp<<" ";
std::cout<<std::endl;
for(int tmp: arr)
std::cout<<tmp<<" ";
std::cout<<std::endl;
for(int i=0;i<n;i++)
int& tmp=arr[i];
tmp=tmp*2;
std::cout<<tmp<<" ";
std::cout<<std::endl;
for(int& tmp:arr)
tmp=tmp*2;
std::cout<<tmp<<" ";
std::cout<<std::endl;
return 0;
使用基于范围的 for 循环, 其 for 循环迭代的范围必须是可确定的:
void func(int arr[]) //形参中的数组不是数组,而是指针变量,存放数组的地址,无法确定元素个数
//使用基于范围的 for 循环, 其 for 循环迭代的范围必须是可确定的:
for(int& tmp:arr)
std::cout<<tmp<<" ";
int arr[]=1,2,3,4,5,6,7,8,9,10;
func(arr);//传过去的是arr数组的地址
3.3 静态断言
C/C++提供了调试工具 assert, 这是一个宏, 用于在运行阶段对断言进行检查, 如果条件为真, 执行程序, 否则调用 abort()。
#include<iostream>
#include<assert.h>
int main()
//C/C++提供了调试工具 assert, 这是一个宏, 用于在运行阶段对断言进行检查, 如果条件为真, 执行程序, 否则调用 abort()。
bool flag=false;
int a=10;
//运行时检查条件,如果为真,往下执行,如果为假,中断提示错误
//assert(flag==true); //条件为假,中断
assert(flag==false); //条件为真,执行
std::cout<<a<<std::endl;
std::cout << "1" << std::endl;
std::cout <<"c++11"<<std::endl;
return 0;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bsJ9a7wj-1664897582183)(C:\\Users\\hp\\AppData\\Roaming\\Typora\\typora-user-images\\image-20220917193143153.png)]
C++ 11 新增了关键字 static_assert, 可用于在编译阶段对断言进行测试。静态断言的好处:
- 更早的报告错误, 我们知道构建是早于运行的, 更早的错误报告意味着开发成本的降低。
- 减少运行时调用堆栈开销, 静态断言是编译期检测的, 减少了运行时开销 。
语法如下:
- static_assert(常量表达式, 提示字符串)。注意: 只能是常量表达式, 不能是变量 。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rxSqsF9n-1664897582185)(C:\\Users\\hp\\AppData\\Roaming\\Typora\\typora-user-images\\image-20220917195154977.png)]
int main(void)
//static_asset(常量表达式条件,"提示的字符串")
//static_assert(sizeof(void* )==4,"64位系统不支持");
static_assert(sizeof(void* )==8,"64位系统支持");
std::cout<<sizeof(void*)<<std::endl;
return 0;
3.4 noexcept 修饰符(vs2013 不支持)
代表此函数不能抛出异常, 如果抛出, 就会异常C++11 使用 noexcept 替代 throw() 。
#include<iostream>
void func01()
throw 1;
//这个函数不能抛出任何异常
void func02() throw()
throw 1;
//这个函数只能抛出int、char类型异常
//C++11 已经弃用这个声明
void func03() throw(int,char)
//这个函数不能抛出任何异常
void func04() noexcept
throw 1;
int main()
func01();
func02();
func03();
func04();
return 0;
说明代码:
PS D:\\C++11代码\\build> cmake --build .
用于 .NET Framework 的 Microsoft (R) 生成引擎版本 16.7.0+b89cb5fde
版权所有(C) Microsoft Corporation。保留所有权利。
noexcept修饰符.cpp
D:\\C++11代码\\易用性的改进\\noexcept修饰符\\noexcept修饰符.cpp(9,1): warning C4297: “func02”: 假定函数不引发异常,
但确实发生了 [D:\\C++11代码\\build
\\易用性的改进\\noexcept修饰符\\main.vcxproj]
D:\\C++11代码\\易用性的改进\\noexcept修饰符\\noexcept修饰符.cpp(9,1): message : 在函数上指定了 __declspec(nothrow)、
throw()、noexcept(true
)或 noexcept [D:\\C++11代码\\build\\易用性的改进\\noexcept修饰符\\main.vcxproj]
D:\\C++11代码\\易用性的改进\\noexcept修饰符\\noexcept修饰符.cpp(14,29): warning C4290: 忽略 C++ 异常规范,但指示函数
不是 __declspec(nothrow) [D
:\\C++11代码\\build\\易用性的改进\\noexcept修饰符\\main.vcxproj]
D:\\C++11代码\\易用性的改进\\noexcept修饰符\\noexcept修饰符.cpp(20,1): warning C4297: “func04”: 假定函数不引发异常,
但确实发生了 [D:\\C++11代码\\buil
d\\易用性的改进\\noexcept修饰符\\main.vcxproj]
D:\\C++11代码\\易用性的改进\\noexcept修饰符\\noexcept修饰符.cpp(20,1): message : 在函数上指定了 __declspec(nothrow)
、throw()、noexcept(tru
e)或 noexcept [D:\\C++11代码\\build\\易用性的改进\\noexcept修饰符\\main.vcxproj]
main.vcxproj -> D:\\C++11代码\\build\\易用性的改进\\noexcept修饰符\\Debug\\main.exe
PS D:\\C++11代码\\build>
3.5 nullptr
nullptr 是为了解决原来 C++中 NULL 的二义性问题而引进的一种新的类型, 因为 NULL 实际上代表的是 0。
#include<iostream>
void func(int p)
std::cout<<__LINE__<<std::endl;
void func(int* p)
std::cout<<__LINE__<<std::endl;
int main()
int *a=NULL;
int *b=0;
//下面func都是调用第一个参数是整形的func,而不是指针的func
func(0); //调用 func(int), 就算写 NULL, 也是调用这个
func(NULL);
int* c=nullptr; //nullptr只能给指针赋值
if(a==c)
std::cout<<"equal"<<std::endl;
func(nullptr); //不能赋值给常量,这里报错
return 0;
输出结果:
PS D:\\C++11代码\\build\\易用性的改进\\nullptr\\Debug> .\\main.exe
4
4
equal
8
PS D:\\C++11代码\\build\\易用性的改进\\nullptr\\Debug>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n90VhsG6-1664897582185)(C:\\Users\\hp\\AppData\\Roaming\\Typora\\typora-user-images\\image-20220917211018068.png)]
nullptr只能赋值给指针,不能赋值给常量。
int tmp=nullptr;
输出结果:
D:\\C++11代码\\易用性的改进\\nullptr\\nullptr.cpp(22,20): error C2440: “初始化”: 无法从“nullptr”转换为“int” [D:\\C++11代码\\build\\易用性的改进
\\nullptr\\main.vcxproj]
D:\\C++11代码\\易用性的改进\\nullptr\\nullptr.cpp(22,12): message : 本机 nullptr 只能转换为 bool 或使用 reinterpret_cast 转换为整型 [D:\\C+
3.6 强类型枚举
C++ 11 引入了一种新的枚举类型, 即“枚举类”, 又称“强类型枚举”。 声明请类型枚举非常简单, 只需要在 enum 后加上使用 class 或 struct。 如:
enum OldYes, No; // old style
enum class NewYes, No; // new style
enum struct New2Yes, No; // new style
“传统”的 C++枚举类型有一些缺点: 它会在一个代码区间中抛出枚举类型成员(如果在相同的代码域中的两个枚举类型具有相同名字的枚举成员, 这会导致命名冲突) , 它们会被隐式转换为整型, 并且不可以指定枚举的底层数据类型。
#include <iostream>
enum Flag1
OK,
ERROR
;
enum Flag2
OK,
ERROR
;
int main()
Flag1 flag=OK;
return 0;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V07RYZ8L-1664897582186)(C:\\Users\\hp\\AppData\\Roaming\\Typora\\typora-user-images\\image-20220917213511329.png)]
输出结果:
PS D:\\C++11代码\\build> cmake --build .
用于 .NET Framework 的 Microsoft (R) 生成引擎版本 16.7.0+b89cb5fde
版权所有(C) Microsoft Corporation。保留所有权利。
强类型枚举.cpp
D:\\C++11代码\\易用性的改进\\强类型枚举\\强类型枚举.cpp(12,9): error C2365: “main::OK”: 重定义;以前的定义是“枚举数” [D:\\C++11代码\\build\\易用性的改进\\强类型枚
举\\main.vcxproj]
D:\\C++11代码\\易用性的改进\\强类型枚举\\强类型枚举.cpp(7): message : 参见“main::OK”的声明 [D:\\C++11代码\\build\\易用
性的改进\\强类型枚举\\main.vcxproj]
D:\\C++11代码\\易用性的改进\\强类型枚举\\强类型枚举.cpp(13,9): error C2365: “main::ERROR”: 重定义;以前的定义是“枚举
数” [D:\\C++11代码\\build\\易用性的改进\\强
类型枚举\\main.vcxproj]
D:\\C++11代码\\易用性的改进\\强类型枚举\\强类型枚举.cpp(8): message : 参见“main::ERROR”的声明 [D:\\C++11代码\\build\\易
用性的改进\\强类型枚举\\main.vcxproj]
PS D:\\C++11代码\\build>
普通枚举的类型大小为4。
#include <iostream>
enum Flag1
OK,
ERROR
;
// enum Flag2
//
// OK,
// ERROR
// ;
int main()
Flag1 flag=OK;
std::cout<<sizeof(OK)<<std::endl;
std::cout<<sizeof(Flag1)<<std::endl;
return 0;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NmJzA1ef-1664897582187)(C:\\Users\\hp\\AppData\\Roaming\\Typora\\typora-user-images\\image-20220917214224829.png)]
强类型枚举:强类型枚举,enum后面加上class或者struct修饰。
#include <iostream>
enum Flag1
OK,
ERROR
;
// enum Flag2
//
// OK,
// ERROR
// ;
//强类型枚举,enum后面加上class或者struct修饰
enum class Grade1
Success,
Failed,
Equal
;
//强类型枚举,enum后面加上class或者struct修饰
enum struct Grade2
Success,
Failed,
Equal
;
//强枚举类型可以指定枚举常量的类型
enum class Grade3:char
Success,
Failed,
Equal
;
//强枚举类型可以指定枚举常量的类型
enum class Grade4:int
Success,
Failed,
Equal
;
int main()
Flag1 flag=OK;
std::cout<<sizeof(OK)<<std::endl;
std::cout<<sizeof(Flag1)<<std::endl;
//Grade1 grade=Success; err 必须指定枚举常量的作用域
Grade1 grade=Grade1::Success;
std::cout<<sizeof(Grade3::Success)<<std::endl;
std::cout<<sizeof(Grade4::Success)<<std::endl;
return 0;
输出结果:
PS D:\\C++11代码\\build\\易用性的改进\\强类型枚举\\Debug> .\\main.exe
4
4
1
4
PS D:\\C++11代码\\build\\易用性的改进\\强类型枚举\\Debug>
3.6 常量表达式(vs2013 不支持)
3.6.1
常量表达式主要是允许一些计算发生在编译时, 即发生在代码编译而不是运行的时候这是很大的优化: 假如有些事情可以在编译时做, 它将只做一次, 而不是每次程序运行时都计算 。
#include<iostream>>
enum Grade
Ok,
Error
;
int getNum1()
return Grade::Ok;
int main()
//枚举常量初始化,必须是整形常量
enum e1=getNum1(),e2;
return 0;
输出结果:
PS D:\\C++11代码\\build> cmake --build .
用于 .NET Framework 的 Microsoft (R) 生成引擎版本 16.7.0+b89cb5fde
版权所有(C) Microsoft Corporation。保留所有权利。
Checking Build System
Building Custom Rule D:/C++11????/?????????/????????/CMakeLists.txt
常量表达式.cpp
D:\\C++11代码\\易用性的改进\\常量表达式\\常量表达式.cpp(1,1): warning C4819: 该文件包含不能在当前代码页(936)中表示的
字符。请将该文件保存为 Unicode 格式以防止数据丢失 [D:\\
C++11代码\\build\\易用性的改进\\常量表达式\\main.vcxproj]
D:\\C++11代码\\易用性的改进\\常量表达式\\常量表达式.cpp(1,9): warning C4067: 预处理器指令后有意外标记 - 应输入换行符
[D:\\C++11代码\\build\\易用性的改进\\常量表达式\\mai
n.vcxproj]
D:\\C++11代码\\易用性的改进\\常量表达式\\常量表达式.cpp(14,21): error C2131: 表达式的计算结果不是常数 [D:\\C++11代码\\build\\易用性的改进\\常量表达式\\main.vcxproj
]
D:\\C++11代码\\易用性的改进\\常量表达式\\常量表达式.cpp(14,21): message : 对未定义的函数或为未声明为“constexpr”的函
数的调用导致了故障 [D:\\C++11代码\\build\\易用性的改
进\\常量表达式\\main.vcxproj]
D:\\C++11代码\\易用性的改进\\常量表达式\\常量表达式.cpp(14,21): message : 请参见“getNum1”的用法 [D:\\C++11代码\\build\\易用性的改进\\常量表达式\\main.vcxproj
]
PS D:\\C++11代码\\build>
另一个例子:
#include<iostream>>
enum Grade
Ok,
Error
;
int getNum1()
return Grade::Ok;
const int getNum2()
return Grade::Error;
int main()
//枚举常量初始化,必须是整形常量
//enum e1=getNum1(),e2;
enume1=getNum2(),e2; //加了const也不行
return 0;
输出结果:
PS D:\\C++11代码\\build> cmake --build .
用于 .NET Framework 的 Microsoft (R) 生成引擎版本 16.7.0+b89cb5fde
版权所有(C) Microsoft Corporation。保留所有权利。
常量表达式.cpp
D:\\C++11代码\\易用性的改进\\常量表达式\\常量表达式.cpp(1,1): warning C4819: 该文件包含不能在当前代码页(936)中表示的
字符。请将该文件保存为 Unicode 格式以防止数据丢失 [D:\\
C++11代码\\build\\易用性的改进\\常量表达式\\main.vcxproj]
D:\\C++11代码\\易用性的改进\\常量表达式\\常量表达式.cpp(1,9): warning C4067: 预处理器指令后有意外标记 - 应输入换行符
[D:\\C++11代码\\build\\易用性的改进\\常量表达式\\mai
n.vcxproj]
D:\\C++11代码\\易用性的改进\\常量表达式\\常量表达式.cpp(18,20): error C2131: 表达式的计算结果不是常数 [D:\\C++11代码\\build\\易用性的改进\\常量表达式\\main.vcxproj
]
D:\\C++11代码\\易用性的改进\\常量表达式\\常量表达式.cpp(18,20): message : 对未定义的函数或为未声明为“constexpr”的函
数的调用导致了故障 [D:\\C++11代码\\build\\易用性的改
进\\常量表达式\\main.vcxproj]
D:\\C++11代码\\易用性的改进\\常量表达式\\常量表达式.cpp(18,20): message : 请参见“getNum2”的用法 [D:\\C++11代码\\build\\易用性的改进\\常量表达式\\main.vcxproj
]
PS D:\\C++11代码\\build>
使用 constexpr, 你可以创建一个编译时的函数:
#include<iostream>>
enum Grade
Ok,
Error
;
int getNum1()
return Grade::Ok;
const int getNum2()
return Grade::Error;
//常量表达式,发生在编译阶段
constexpr int getNum3()
return Grade::Ok;
int main()
//枚举常量初始化,必须是整形常量
//enum e1=getNum1(),e2; //枚举类型,err
//enume1=getNum2(),e2; //err
enum flage1=getNum3(),e2;
constexpr int tmp=getNum3(); //ok,发生在编译阶段,而不是函数运行阶段
enum a1=tmp,a; //ok
std::cout<<flag::e1<<std::endl;
std::cout<<tmp<<std::endl;
return 0;
输出结果:
PS D:\\C++11代码\\build\\易用性的改进\\常量表达式\\Debug> .\\main.exe
0
0
PS D:\\C++11代码\\build\\易用性的改进\\常量表达式\\Debug>
3.6.2 constexpr 函数的限制
- 函数中只能有一个 return 语句(有极少特例)。
- 函数必须返回值(不能是 void 函数)。
- 在使用前必须已有定义。
- return 返回语句表达式中不能使用非常量表达式的函数、 全局数据, 且必须是一个常量
表达式 。
函数中只能有一个 return 语句。
/*
constexpr函数的限制:
函数中只能有一个return语句(有极少特例)
函数必须返回值(不能是void函数)
在使用前必须已有定义
return返回语句表达式中不能使用非常量表达式的函数、全局数据,且必须是一个常量表达式
*/
int add(int x,int y)
return x+y;
constexpr int func1()
//err 函数中只能有一个return语句
int b=add(1,2);
constexpr int a=1;
return a+b;
输出结果:
PS D:\\C++11代码\\build> cmake ..
CMake Deprecation Warning at CMakeLists.txt:1 (cmake_minimum_required):
Compatibility with CMake < 2.8.12 will be removed from a future version of
Update the VERSION argument <min> value or use a ...<max> suffix to tell
CMake that the project does not need compatibility with older versions.
-- Selecting Windows SDK version 10.0.18362.0 to target Windows 10.0.19042.
-- Configuring done
-- Generating done
-- Build files have been written to: D:/C++11代码/build
PS D:\\C++11代码\\build> cmake --build .
用于 .NET Framework 的 Microsoft (R) 生成引擎版本 16.7.0+b89cb5fde
版权所有(C) Microsoft Corporation。保留所有权利。
constexpr函数的限制.cpp
D:\\C++11代码\\易用性的改进\\常量表达式\\constexpr函数的限制.cpp(1,1): warning C4819: 该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode 格式以防止
数据丢失 [D:\\C++11代码\\build\\易用性的改进\\常量表达式\\main.vcxproj]
D:\\C++11代码\\易用性的改进\\常量表达式\\constexpr函数的限制.cpp(14,15): error C3615: constexpr 函数 "func1" 不会生
成常数表达式 [D:\\C++11代码\\buil
d\\易用性的改进\\常量表达式\\main.vcxproj]
D:\\C++11代码\\易用性的改进\\常量表达式\\constexpr函数的限制.cpp(15,14): message : 对未定义的函数或为未声明为“constexpr”的函数的调用导致了故障 [D:\\C++11代码\\bu
ild\\易用性的改进\\常量表达式\\main.vcxproj]
D:\\C++11代码\\易用性的改进\\常量表达式\\constexpr函数的限制.cpp(15,14): message : 请参见“add”的用法 [D:\\C++11代码\\build\\易用性的改进\\常量表达式\\main.vc
xproj]
PS D:\\C++11代码\\build>
函数中只能有一个 return 语句(有极少特例)。
#include<iostream>
/*
constexpr函数的限制:
函数中只能有一个return语句(有极少特例)
函数必须返回值(不能是void函数)
在使用前必须已有定义
return返回语句表达式中不能使用非常量表达式的函数、全局数据,且必须是一个常量表达式
*/
int add(int x,int y)
return x+y;
constexpr int func1()
//err 函数中只能有一个return语句
//int b=add(1,2);
constexpr int a=1;
return a;
constexpr int func2()
//函数中只能有一个return语句(有极少特例)
//允许包含typedef,using指令,静态断言
static_assert(1,"fail");
constexpr int a=1;
return a;
int main()
//std::cout<<func1()<<std::endl;
std::cout<<func1()<<std::endl;
return 0;
输出结果:
PS D:\\C++11代码\\build\\易用性的改进\\常量表达式\\Debug> .\\main.exe
1
PS D:\\C++11代码\\build\\易用性的改进\\常量表达式\\Debug>
函数必须返回值(不能是 void 函数)。constexpr常量表达式必须有返回值。
return 返回语句表达式中不能使用非常量表达式的函数,且必须是一个常量
表达式 。
#include<iostream>
/*
constexpr函数的限制:
函数中只能有一个return语句(有极少特例)
函数必须返回值(不能是void函数)
在使用前必须已有定义
return返回语句表达式中不能使用非常量表达式的函数、全局数据,且必须是一个常量表达式
*/
int add(int x,int y)
return x+y;
constexpr int func1()
//err 函数中只能有一个return语句
//int b=add(1,2);
constexpr int a=1;
return a;
constexpr int func2()
//函数中只能有一个return语句(有极少特例)
//允许包含typedef,using指令,静态断言
static_assert(1,"fail");
constexpr int a=1;
return a;
int x=3;
constexpr int func3()
return x; //返回全局变量
constexpr int func4()
return add(1,2);
int main()
//std::cout<<func1()<<std::endl;
//std::cout<<func1()<<std::endl;
std::cout<<func3()<<std::endl;
return 0;
输出结果:
PS D:\\C++11代码\\build> cmake --build .
用于 .NET Framework 的 Microsoft (R) 生成引擎版本 16.7.0+b89cb5fde
版权所有(C) Microsoft Corporation。保留所有权利。
constexpr函数的限制.cpp
D:\\C++11代码\\易用性的改进\\常量表达式\\constexpr函数的限制.cpp(1,1): warning C4819: 该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode 格式以防止
数据丢失 [D:\\C++11代码\\build\\易用性的改进\\常量表达式\\main.vcxproj]
PS D:\\C++11代码\\build> cmake --build .
用于 .NET Framework 的 Microsoft (R) 生成引擎版本 16.7.0+b89cb5fde
版权所有(C) Microsoft Corporation。保留所有权利。
constexpr函数的限制.cpp
D:\\C++11代码\\易用性的改进\\常量表达式\\constexpr函数的限制.cpp(1,1): warning C4819: 该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode 格式以防止
数据丢失 [D:\\C++11代码\\build\\易用性的改进\\常量表达式\\main.vcxproj]
main.vcxproj -> D:\\C++11代码\\build\\易用性的改进\\常量表达式\\Debug\\main.exe
PS D:\\C++11代码\\build> cmake --build .
用于 .NET Framework 的 Microsoft (R) 生成引擎版本 16.7.0+b89cb5fde
版权所有(C) Microsoft Corporation。保留所有权利。
constexpr函数的限制.cpp
D:\\C++11代码\\易用性的改进\\常量表达式\\constexpr函数的限制.cpp(1,1): warning C4819: 该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode 格式以防止
数据丢失 [D:\\C++11代码\\build\\易用性的改进\\常量表达式\\main.vcxproj]
D:\\C++11代码\\易用性的改进\\常量表达式\\constexpr函数的限制.cpp(35,15): error C3615: constexpr 函数 "func4" 不会生
成常数表达式 [D:\\C++11代码\\buil
d\\易用性的改进\\常量表达式\\main.vcxproj]
D:\\C++11代码\\易用性的改进\\常量表达式\\constexpr函数的限制.cpp(37,15): message : 对未定义的函数或为未声明为“constexpr”的函数的调用导致了故障 [D:\\C++11代码\\bu
ild\\易用性的改进\\常量表达式\\main.vcxproj]
D:\\C++11代码\\易用性的改进\\常量表达式\\constexpr函数的限制.cpp(37,15): message : 请参见“add”的用法 [D:\\C++11代码\\build\\易用性的改进\\常量表达式\\main.vc
xproj]
PS D:\\C++11代码\\build>
在使用前必须已有定义。
int main()
constexpr int func(); //函数声明, 定义放在 main 函数后面
constexpr int c = func(); //err, 无法通过编译, 在使用前必须已有定义
return 0;
constexpr int func()
return 1;
3.6.3 类中成员函数是常量表达式
常量表达式的构造函数有以下限制:
- 构造函数体必须为空。
- 初始化列表只能由常量表达式来赋值 。
#include<iostream>
class Date
public:
//constexpr修饰构造函数,构造函数体必须为空
constexpr Date(int year,int month,int day)
:year_(year)
,month_(month)
,day_(day)
//构造函数体必须为空,那么不能使用赋值初始化,而是使用参数列表初始化
~Date()
constexpr int getYear()
return year_;
constexpr int getMonth()
return month_;
constexpr int getDay()
return day_;
private:
int year_;
int month_;
int day_;
;
int main()
Date date(2022,9,18); //必须使用常量给构造函数传参
std::cout<<date.getYear()<<std::endl;
std::cout<<date.getMonth()<<std::endl;
std::cout<<date.getDay()<<std::endl;
return 0;
输出结果:
PS D:\\C++11代码\\build\\易用性的改进\\常量表达式\\Debug> .\\main.exe
2022
9
18
PS D:\\C++11代码\\build\\易用性的改进\\常量表达式\\Debug>
3.7 用户定义字面量(vs2013 不支持)
用户自定义字面值, 或者叫“自定义后缀”更直观些, 主要作用是简化代码的读写。
#include <iostream>
using namespace std;
//用户自定义字面值, 或者叫“自定义后缀”更直观些, 主要作用是简化代码的读写。
//自定义变量,名字要求operator"" xxx
long double operator"" _mm(long double x)
return x / 1000;
long double operator"" _m(long double x)
return x;
long double operator"" _km(long double x)
return x * 1000;
int main()
cout << 1.0_mm << endl; // 0.001
cout << 1.0_m << endl; // 1
cout << 1.0_km << endl; // 1000
return 0;
输出结果:
PS D:\\C++11代码\\build\\易用性的改进\\常量表达式\\Debug> .\\main.exe
0.001
1
1000
PS D:\\C++11代码\\build\\易用性的改进\\常量表达式\\Debug>
根据 C++ 11 标准, 只有下面参数列表才是合法的:
//根据 C++ 11 标准, 只有下面参数列表才是合法的:
/*
char const *
unsigned long long
long double
char const *, size_t
wchar_t const *, size_t
char16_t const *, size_t
char32_t const *, size_t
*/
最后四个对于字符串相当有用, 因为第二个参数会自动推断为字符串的长度。 例如:
#include <iostream>
using namespace std;
//用户自定义字面值, 或者叫“自定义后缀”更直观些, 主要作用是简化代码的读写。
//自定义变量,名字要求operator"" xxx
long double operator"" _mm(long double x)
return x / 1000;
long double operator"" _m(long double x)
return x;
long double operator"" _km(long double x)
return x * 1000;
//根据 C++ 11 标准, 只有下面参数列表才是合法的:
/*
char const *
unsigned long long
long double
char const *, size_t
wchar_t const *, size_t
char16_t const *, size_t
char32_t const *, size_t
*/
//自定义变量,名字要求operato"" xxx
//只需要给第一个参数传参,第二个参数自动推算,测第一个参数的长度,给第二个参数赋值
size_t operator"" _len(char const* str,size_t n)
return n;
char const* operator"" _str(char const* buf,size_t n)
return buf;
char const* operator"" _test(char const* tmp)
return tmp;
int main()
cout << 1.0_mm << endl; // 0.001
cout << 1.0_m << endl; // 1
cout << 1.0_km << endl; // 1000
cout<<"abc "_len<<endl;
cout<<"abc d"_str<<endl;
cout<<1_test<<endl; //注意这个
return 0;
输出结果:
PS D:\\C++11代码\\build\\易用性的改进\\常量表达式\\Debug> .\\main.exe
0.001
1
1000
4
abc d
1
PS D:\\C++11代码\\build\\易用性的改进\\常量表达式\\Debug>
*注意这个,对于参数 char const , 应该被称为原始字面量 raw literal 操作符。 例如 :
char const* operator"" _test(char const* tmp)
return tmp;
cout<<1_test<<endl; //注意这个
输出结果:
1
3.8 原生字符串字面值 (原始字符串字面值 )
原生字符串字面值(raw string literal)使用户书写的字符串“所见即所得”。 C++11 中原生字符串的声明相当简单, 只需在字符串前加入前缀, 即字母 R, 并在引号中使用括号左右标识,就可以声明该字符串字面量为原生字符串了 。
#include<iostream>
#include<string>
int main()
std::cout<<R"(c++11 \\n
fighting \\n)";
std::string str=R"(love is beautiful,are you love me?
yes,i can not!\\n)";
std::cout<<str<<std::endl;
return 0;
输出结果:
PS D:\\C++11代码\\build\\易用性的改进\\常量表达式\\Debug> .\\main.exe
c++11 \\n
fighting \\nlove is beautiful,are you love me?
yes,i can not!\\n
PS D:\\C++11代码\\build\\易用性的改进\\常量表达式\\Debug>
4、类的改进
4.1 继承构造(vs2013 不支持)
C++ 11 允许派生类继承基类的构造函数(默认构造函数、 复制构造函数、 移动构造函数除外) 。
#include<iostream>
class A
public:
A(int a,int b)
:a_(a)
,b_(b)
protected:
int a_;
int b_;
;
class B : public A
public:
//通过参数列表给构造函数传参
B(int x,int y)
:A(x,y)
void Display()
std::cout<<"a= "<<a_<<" b= "<<b_<<std::endl;
;
int main()
B b(10,20);
b.Display();
return 0;
输出结果:
PS D:\\C++11代码\\build\\类的改进\\Debug> .\\main.exe
a= 10 b= 20
注意:
- 继承的构造函数只能初始化基类中的成员变量, 不能初始化派生类的成员变量。
- 如果基类的构造函数被声明为私有, 或者派生类是从基类中虚继承, 那么不能继承构造
函数。 - 一旦使用继承构造函数, 编译器不会再为派生类生成默认构造函数 。
#include<iostream>
class A
public:
A(int a,int b)
:a_(a)
,b_(b)
protected:
int a_;
int b_;
;
class B : public A
public:
#if 0
//通过参数列表给构造函数传参
B(int x,int y)
:A(x,y)
#endif
using A::A; // 继承构造函数
void Display()
std::cout<<"a= "<<a_<<" b= "<<b_<<std::endl;
;
int main()
B b(10,20);
b.Display();
return 0;
输出结果:
PS D:\\C++11代码\\build\\类的改进\\Debug> .\\main.exe
a= 10 b= 20
4.2 委托构造
和继承构造函数类似, 委托构造函数也是 C++11 中对 C++的构造函数的一项改进, 其目的也是为了减少程序员书写构造函数的时间。
如果一个类包含多个构造函数, C++ 11 允许在一个构造函数中的定义中使用另一个构造函数, 但这必须通过初始化列表进行操作, 如下:
#include<iostream>
//继承构造函数类似, 委托构造函数也是 C++11 中对 C++的构造函数的一项改进, 其目的也是为了减少程序员书写构造函数的时间。
//如果一个类包含多个构造函数, C++ 11 允许在一个构造函数中的定义中使用另一个构造函数, 但这必须通过初始化列表进行操作, 如下:
class Test
public:
Test()
: Test(1,'a')
// Test(int x)
// : x_(x)
//
Test(int x)
: Test(x,'b')
// Test(char c)
// :c_(c)
//
Test(char c)
:Test(100,c)
Test(int x,char c)
:x_(x)
,c_(c)
int x_;
char c_;
;
int main()
Test test1; //Test():Test(1,'a')
Test test2(10); //Test(int x):Test(x,'b')
Test test3('c'); //Test(char c):Test(100,c)
std::cout<<test1.x_<<std::endl;
std::cout<<test1.c_<<std::endl;
std::cout<<test2.x_<<std::endl;
std::cout<<test2.c_<<std::endl;
std::cout<<test3.x_<<std::endl;
std::cout<<test3.c_<<std::endl;
return 0;
输出结果:
PS D:\\C++11代码\\build\\类的改进\\Debug> .\\main.exe
1
a
10
b
100
c
PS D:\\C++11代码\\build\\类的改进\\Debug>
4.3 继承控制: final 和 override
C++11 之前, 一直没有继承控制关键字, 禁用一个类的进一步衍生比较麻烦。C++ 11 添加了两个继承控制关键字: final 和 override。
- final 阻止类的进一步派生和虚函数的进一步重写。
- override 确保在派生类中声明的函数跟基类的虚函数有相同的签名 。
4.3.1 final 阻止类的进一步派生和虚函数的进一步重写
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Nie1xFFl-1664897582188)(C:\\Users\\hp\\AppData\\Roaming\\Typora\\typora-user-images\\image-20220918191555928.png)]
#include<iostream>
//final修饰类防止类进一步继承,修饰虚函数防止虚函数被重写
class A final //加上final,表明A类不能被继承,也就是不能派生出子类
public:
int a;
;
class B :public A //err,基类不能在派生
public:
;
class C
public:
virtual void func() final; //加上final,修饰虚函数表示虚函数不能被重写
;
class D:public C
public:
virtual void func(); //err,基类中的虚函数用final修饰,不能被重写
;
int main()
return 0;
4.3.2 override 确保在派生类中声明的函数跟基类的虚函数有相同的签名
override 确保在派生类中声明的函数跟基类的虚函数有相同的签名 ,检查子类是否重写了基类的虚函数。
#include<iostream>
class A
public:
//这是第一个虚函数,没有重写,不能用override修饰
virtual void func(int a,char c)=0;
;
class B :public A
public:
//在重写虚函数地方,加上override,要求重写的虚函数与基类一样,就是检查子类虚函数有没有重写
virtual void func(int a,char c) override
;
int main()
return 0;
4.4 类默认函数的控制: “=default” 和 "=delete"函数
4.4.1 "=default"函数
C++ 的类有四类特殊成员函数, 它们分别是: 默认构造函数、 析构函数、 拷贝构造函数以及拷贝赋值运算符。
这些类的特殊成员函数负责创建、 初始化、 销毁, 或者拷贝类的对象。如果程序员没有显式地为一个类定义某个特殊成员函数, 而又需要用到该特殊成员函数时,则编译器会隐式的为这个类生成一个默认的特殊成员函数。
但是, 如果程序员为类显式的自定义了非默认构造函数, 编译器将不再会为它隐式地生成默认无参构造函数。
#include<iostream>
class X
public:
X(); //只声明 外部定义
X()=default; //让编译器提供一个默认的构造函数,效率比用户高
X(int a) //写了带参的构造函数,系统不会提供默认构造函数
:a_(a)
void Display()
std::cout<<X::a_<<std::endl;
protected:
int a_;
;
//default只能修饰类中默认提供的成员函数:无参构造函数、拷贝构造函数、赋值运算符重载函数、析构函数
#if 0
class Y
public:
int y()=default;
int z(int a,int b)=default;
protected:
int a_;
int b_;
;
#endif
X::X()=default; //外部定义也可以
int main()
X obj;
X x(1);
x.Display();
return 0;
原本期望编译器自动生成的默认构造函数却需要程序员手动编写, 即程序员的工作量加大了。 此外, 手动编写的默认构造函数的代码执行效率比编译器自动生成的默认构造函数低。类的其它几类特殊成员函数也和默认构造函数一样, 当存在用户自定义的特殊成员函数时,编译器将不会隐式的自动生成默认特殊成员函数, 而需要程序员手动编写, 加大了程序员的工作量。 类似的, 手动编写的特殊成员函数的代码执行效率比编译器自动生成的特殊成员函数低。
C++11 标准引入了一个新特性: "=default"函数。 程序员只需在函数声明后加上“=default;”,就可将该函数声明为 "=default"函数, 编译器将为显式声明的 "=default"函数自动生成函数体。
"=default"函数特性仅适用于类的特殊成员函数, 且该特殊成员函数没有默认参数。 例如:
class X
public:
int f() = default; // err , 函数 f() 非类 X 的特殊成员函数
X(int, int) = default; // err , 构造函数 X(int, int) 非 X 的特殊成员函数
X(int = 1) = default; // err , 默认构造函数 X(int=1) 含有默认参数
;
"=default"函数既可以在类体里(inline) 定义, 也可以在类体外(out-of-line) 定义。 例如:
class X
public:
X() = default; // Inline defaulted 默认构造函数
X(const X &);
X &operator=(const X &);
~X() = default; // Inline defaulted 析构函数
;
X::X(const X &) = default; // Out-of-line defaulted 拷贝构造函数
X &X::operator=(const X &) = default; // Out-of-line defaulted 拷贝赋值操作符
4.4.2 "=delete"函数
为了能够让程序员显式的禁用某个函数, C++11 标准引入了一个新特性: "=delete"函数。 程序员只需在函数声明后上“=delete;”, 就可将该函数禁用。
#include<iostream>
//delete不同于deafult,delete适用于任何函数,deafult只适用于系统提供的默认成员函数
class X
public:
X() ; //无参构造函数
//X(const X & x) //拷贝构造函数
X(const X & x)=delete; //拷贝构造函数,此函数被禁用
//X& operator=(const X& x) //赋值运算符重载函数
X& operator=(const X& x)=delete; //赋值运算符重载函数,此函数被禁用
~X() //析构函数
//void* operator new(size_t size) //重载new运算符
void* operator new(size_t size)=delete; //重载new运算符,此函数被禁用
//void* operator new[](size_t size) //重载new[]运算符
void* operator new[](size_t size)=delete; //重载new[]运算符,此函数被禁用
;
int main()
X x1;
X x2(x1);
X x3=x1;
X* x1=new X;
X* x2=new X[10];
return 0;
"=delete"函数特性还可用于禁用类的某些转换构造函数, 从而避免不期望的类型转换。
"=delete"函数特性还可以用来禁用某些用户自定义的类的 new 操作符, 从而避免在自由存储区创建类的对象。
5、模板的改进
5.1 右尖括号 > 的改进
在 C++98/03 的泛型编程中, 模板实例化有一个很繁琐的地方, 就是连续两个右尖括号(>>)会被编译解释成右移操作符, 而不是模板参数表的形式, 需要一个空格进行分割, 以避免发生编译时的错误。
#include <iostream>
template <int i>
class X
;
template <class T>
class Y
;
int main()
Y<X<1>> x1; // ok, 编译成功
//template <int i>
Y<X<2>> x2; // err, 编译失败 c++98编译失败
return 0;
;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L8cMJ5cS-1664897582189)(C:\\Users\\hp\\AppData\\Roaming\\Typora\\typora-user-images\\image-20220919221026454.png)]
class X
//...
;
template <class T>
class Y
//...
;
int main()
Y<X<10>> obj; // c++11之前,2个尖括号之间必须有空格
return 0;
在实例化模板时会出现连续两个右尖括号, 同样 static_cast、 dynamic_cast、 reinterpret_cast、const_cast 表达式转换时也会遇到相同的情况。 C++98 标准是让程序员在>>之间填上一个空格, 在 C++11 中, 这种限制被取消了。 在 C++11 标准中, 要求编译器对模板的右尖括号做单独处理, 使编译器能够正确判断出">>"是一个右移操作符还是模板参数表的结束标记。
5.2 模板的别名
c++11中可以用using给类型起别名。
#include<iostream>
#include<type_traits>
//通过typedef 给一个类型起别名,不是新建类型
typedef int int32;
using my_int=int ; //c++11的方式
int main()
//is_same是判断两个类型是否一致,如果是则返回真,否则返回假
std::cout<< std::is_same<int32,my_int>::value<<std::endl;
return 0;
输出结果:
1
5.3 函数模板的默认模板参数
C++11 之前, 类模板是支持默认的模板参数, 却不支持函数模板的默认模板参数:
#include<iostream>
//1、 普通函数带默认参数, c++98 编译通过, c++11 编译通过
void DefParm(int m = 3)
//1、普通函数的默认参数
void func(int a=3)
//2、 类模板是支持默认的模板参数, c++98 编译通过, c++11 编译通过
template <typename T = int>
class DefClass ;
//2、类模板支持默认的模板参数
template<class T,class T2=int> //类模板的模板参数必须是从右往左
class A
;
//3、 函数模板的默认模板参数, c++98 - 编译失败, c++11 - 编译通过
template <typename T = int> void DefTempParm()
//3、C++11才支持,函数模板带默认的模板参数
// 函数模板的参数可以从左往右,也可以从右往左
template<class T=int,class T2> void func2(T a,T2 b)
int main()
return 0;
类模板的默认模板参数必须从右往左定义,函数模板的默认模板参数则没这个限定:
template<class T1, class T2 = int> class DefClass1;
template<class T1 = int, class T2> class DefClass2; // 无法通过编译
template<class T, int i = 0> class DefClass3;
template<int i 以上是关于C++11新特性精讲(多线程除外)的主要内容,如果未能解决你的问题,请参考以下文章
C++11新特性:6—— C++11对模板实例化中连续右尖括号>>的改进