文章目录
long long 类型
新增了 long long 和 unsigned long long ,支持64bit的整型
在VS中 int和long都是4B,long long 8B
在Linux中 int是4B,long和long long 8B
二、char16_t和 char32_t类型
新增了类型 char16 t和 char32 t,以支持 16 位和 32 位的字符,
意义不大,好像没什么人用,连 demo程序都找不到
原始字面量
原始字面量可以直接表示字符串的实际含义,不需要转义和连接
修饰换行的字符串
语法
R"(字符串的内容)"
R"aaa(字符串的内容)aaa" //可以在括号前后加标签 无实际作用,类似注释 但是必须成对出现
string l = R"(G:\code\c++)";
cout<<l<<endl;
统一的初始化
C++11 丰富了大括号的使用范围,用大括号括起来的列表(统一的初始化列表)可以用于所有内置类型和用户自定义类型。使用统一的初始化列表时,可以添加等号(=),也可以不添加:
int x{10};
double y{3.14};
int q[5]{1,2,3,4,5};
自动推导类型 auto
在C和C++98中,auto 关键字用于修饰变量(自动存储的局部变量)
在 C++11 中,赋予了 auto 全新的含义,不再用于修饰的变量,而是作为一个类型指示符,指示编译器在编译时推导 auto 声明的变量的数据类型。
auto一般忽略顶层const ,忽略底层const(对常量对象取地址是一种底层const操作)
设置一个类型为auto的引用时,初始值的顶层const保留
auto a = 50;
auto b = "你好";
const int i = 10;
auto a = i;//a是一个整数 顶层的const被忽略了
a=100;//正确的
const int *p = &i;
auto q = p;//q的类型是 const int*
*q = 10;//报错 不允许修改
注意
- 使用auto必须在定义的时候初始化
- 初始化的右值可以是具体的右值,也可以是表达式或者函数返回值
- auto不能作为函数的形参类型
- auto不能直接声明数组
- auto不能定义类的非静态成员变量
- auto不要乱用、滥用
真正用途
- 代替冗长复杂的变量声明:迭代器、函数指针等等
- 在模板中,用于声明依赖模板参数的变量
- 函数模板依赖模板参数的返回值
- 用于lambda表达式中
int f(int a,double b,const char *c,long long d,short e){
cout<<11<<endl;
return 0;
}
int main() {
auto a = 50;
auto b = "你好";
auto pf = f; //和下面的效果是一样的
int (*pf1)(int,double,const char *,long long,short) = f;
pf(1,1.1,"你好",1,1);
pf1(1,1.2,"你好",1,1);
}
函数模板高级
decltype关键字
decltype
关键字用于查询表达式的数据类型
语法:decltype(expression) var
decltype分析表达式并得到其类型,不会计算执行表达式
规则:要么是expression类型,yaom是expression类型的引用
- expression中没有括号,则var类型和expression的类型完全相同,包括const等限定符
const int a = 1;
decltype(a) c = 5;//c的类型也是 const int
- expression是函数调用,则var类型是函数返回值的类型(不可以是void 但是可以是void*)
int fun2(){
return 1;
}
decltype(fun2())d; //不会调用fun2
//fun2 和 fun2() 不一样
- expression是左值(能取地址)(要排除第一种情况)、或者用括号括起来的标识符,则var的类型是expression的引用
int a = 10;
decltype(++a) e = a;
decltype((a)) e = a;
- 上述情况都不满足,则var类型和expression相同
函数后置返回类型
int fun(int x,double y){}
//等同于
auto fun(int x,double y) -> int{}
auto fun(int x,double y) -> decltype(x+y){}
//但是C++14标准 函数返回值允许是auto,不必尾随返回类型
auto fun(int x,double y){}
auto是一个占位符
模板的别名
空指针nullptr
空指针是不会指向有效数据的指针
以前,C/C++用0表示空指针,这带来了一些问题,这样的话0 既可以表示指针常量,又可以表示整型常量
C++11 新增了关键字 nullptr,用于表示空指针 它是指针类型,不是整型类型
为了向后兼容,C++11仍允许用0来表示空指针,因此表达式nullptr==0为true
使用 nullptr 提供了更高的类型安全
例如,可以将0传递给形参为int的函数,但是,如果将 nullptr 传递给这样的函数,编译器将视为错误
出于清晰和安全考虑使用nullptr
智能指针
explicit关键字
C++支持对象自动转换,但是,自动类型转换可能导致意外
为了解决这种问题,C++11引入了explicit 关键字,用于关闭自动转换的特性
类内成员初始化
class Person{
public:
int age=18;
string name="西施";
};
基于范围的for循环
语法
for(迭代变量:迭代范围){
//循环体
}
std::vector<int>v({1,2,34,55,68,1,23});
for(auto it:v){
cout<<it<<endl;
}
注意事项:
- 迭代的范围可以是数组名、容器名、初始化列表或者可迭代的对象(支持 begin()、end()、++)
- 数组名传入函数后,已退化成指针,不能作为容器名。
- 如果容器中的元素是结构体和类,迭代器变量应该电明为引用,加 const 约束表示只读
- 注意迭代器失效的问题
新的STL容器
array 静态数组
forward_list 单向链表
unordered_map、unordered_set 哈希表
嵌套模板尖括号
finial关键字
final 关键字用于限制某个类不能被继承,或者某个虚函数不能被重写
final 关键字放在类名或虚函数名的后面
override关键字
在派生类中,把 override 放在成员函数的后面,表示重写基类的虚函数,提高代码的可读性
在派生类中,如果某成员函数不是重写基类的虚函数,随意的加上 override 关键字,编译器会报错
数值类型和字符串之间的转化
数值到字符串
字符串到数值
静态断言
常量表达式constexpr关键字
const既可以表示只读,也可以表示常量
常量在编译过程就已经知道值
C++11 标准为了解决 const 关键字的双重语义问题,保留了 const 表示“只读”的语义,而将“常量”的语义划分给了新添加的 constexpr 关键字
所以,C++11 标准中,建议将const和 constexpr 的功能区分开,表达“只读”语义的场景用 const,表达“常量”语义的场景用 constexpr
const int *p = nullptr;//指向整型常量的指针 *p的值不可变
constexpr int *q = nullptr;//指向整数的常量指针 顶层const q的值不可变
constexpr const int *x = nullptr;//x和*x都不可变
默认函数控制
C++自定义的类有默认生成一些成员函数:
- 无参构造函数
- 拷贝构造函数
- 拷贝赋值函数
- 移动构造函数
- 移动赋值函数
- 析构函数
=default 是启动默认函数
=delete 是禁用默认函数
委托构造和继承构造
委托构造
在实际的开发中,为了满足不同的需求,一个类可能会重载多个构造函数。多个构造函数之间可能会有重复的代码。例如变量初始化,如果在每个构造函数中都写一遍,这样代码会显得臃肿
委托构造就是在一个构造函数的初始化列表中调用另一个构造函数
class P {
public:
int _a;
double _b;
std::string _name;
P() : _a(0), _b(0.0), _name("") {}
P(int a, double b) : _a(a), _b(b) {
_name = "";
std::cout << "P(int a,double b)" << std::endl;
}
//如果 P(a, b) 写在里面,那么代表创建一个匿名对象而不是初始化
P(int a, double b, const std::string &name) : P(a, b) {
_name = name;
std::cout << "P(int a,double b,std::string &name)" << std::endl;
}
};
注意
- 不要生成环形的构造过程
- 一旦使用委托构造,就不能在初始化列表中初始化其他成员变量
继承构造
class P {
public:
int _a;
double _b;
std::string _name;
P() : _a(0), _b(0.0), _name("") {}
P(int a, double b) : _a(a), _b(b) {
_name = "";
std::cout << "P(int a,double b)" << std::endl;
}
//如果 P(a, b) 写在里面,那么代表创建一个匿名对象而不是初始化
P(int a, double b, const std::string &name) : P(a, b) {
_name = name;
std::cout << "P(int a,double b,std::string &name)" << std::endl;
}
};
class B:public P{
public:
int b_a;
using P::P;//使用基类的构造函数
B(int a,double b,int c):P(a,b),b_a(c){}//B类有三个构造函数
};
lambda 函数
lambda表达式或匿名函数
特点:距离近、简洁、高效和功能强大
语法:[](形参)->返回类型{}
- 没有参数列表,括号可以不写
- 不能有默认参数,不支持可变参数,所有的参数必须有参数名
- 不写返回类型,自动推断,但是推荐显式的指定
捕获列表
通过捕获列表,lambda函数可以访问父作用域中的非静态局部变量(静态局部变量可以直接访问,不能访问全局变量)
捕获列表书写在[]中,与函数参数传递类似(原理),捕获的方式可以是值和引用
lambda函数本质就是标准委员会把仿函数换个花样给大家
右值引用
左值、右值
在C++中所有的值不是左值就是右值
左值:表达式结束后依然存在的持久化对象(有名字的对象)
右值:表达式结束后就不再存在的临时对象(没有名字)
可以取地址的就是左值,否则为右值
C++11拓展了右值:纯右值和将亡值
- 纯右值
- 非引用返回的临时变量
- 运算表达式产生的结果
- 字面常量(C风格字符串除外)
- 将亡值
- 与右值引用相关的表达式
左值引用、右值引用
左值引用(T&):以前所学的引用,只能绑定左值
右值引用(T&&):给右值取个名字 数据类型&& 变量名=右值
,只能绑定右值
//右值引用
int &&a = 10; //10是右值 a和左值没有区别
但是,常量左值(const T&)引用既可以绑定非常量左值、常量左值、右值,而且在绑定右值的时候,常量左值引用还可以像右值引用一样将右值的生命期延长,缺点只读
const int a =5;//常量左值
const int&b = a;
const int&c = 1;//1是右值
引入右值引用的主要目的是实现移动语义
移动语义
如果一个对象中有堆区资源,需要编写拷贝构造函数和赋值函数,实现深拷贝
深拷贝把对象中的堆区资源复制一份,如果源对象(被拷贝的对象)是临时对象,拷贝完就没什么用了,这样会造成无意义的资源申请和释放操作
如果可以直接使用源对象的资源,可以节省资源申请和释放的时间,C++11的移动语义就可以实现
右值调用以下
移动构造函数:类名(类名&& 源对象)
移动赋值函数:类名& operator=(类名&& 源对象)
注意:
- 1)对于一个左值,会调用拷贝构造函数,但是有些左值是局部变量,生命周期也很短,能不能也移动而不是拷贝呢?C++11为了解决这个问题,提供了
std:.move()
方法来将左值转义为右值,从而方便使用移动语义。它其实就是告诉编译器,虽然我是一个左值,但不要对我用拷贝构造函数,用移动构造函数吧。左值对象被转移资源后,不会立刻析构,只有在离开自己的作用域的时候才会析构,如果继续使用左值中的资源,可能会发生意想不到的错误。 - 2)如果没有提供移动构造/赋值函数,只提供了拷贝构造/赋值函数,编译器找不到移动构造/赋值函数就去寻找拷贝构造/赋值函数。
- 3)C++11所有容器都实现了移动语义,避免对含有资源的对象发生无意义的拷贝。
- 4)移动语义对于拥有资源的对象有效,如果是基本类型,使用移动语义无意义。
完美转发
在函数模板中,可以将参数“完美”的转发给其它函数。
所谓完美,即不仅能准确的转发参数的值,还能保证被转发参数的左、右值属性不变
c++11标准引入了右值引用和移动语义,所以,能否实现完美转发,决定了该参数在传递过程使用的是拷贝语义还是移动语义。
- 模板中写为
T&&
,那么函数可以接受左值也可以接受右值 - 提供模板函数
std::forward<T>(参数)
,用于转发参数,右值还是右值,左值还是左值
void fun1(int&i){
std::cout<<"左值"<<std::endl;
}
void fun1(int&&i){
std::cout<<"右值"<<std::endl;
}
void fun2(int i){
fun1(i);
}
template<typename T>
void fun3(T&& i){
fun1(std::forward<T>(i));
}
int main() {
int i=5;
// fun2(i);
// fun2(2);/*左值
// 左值*/
fun2(i);
fun3(2);/*左值
右值*/
可变参数模板
对参数进行泛化,能支持任意个数、任意数据类型的参数
//递归中止时调用非模板函数,函数名要和展开参数包的递归函数相同 无参数
void print(){
std::cout<<"递归结束"<<std::endl;
}
//第一个普通参数 第二个可变参数
template<typename T, typename ...Args>
void print(T arg, Args... args) {//arg本次参数 args尚未展开的参数
std::cout << "本次参数为" << arg << std::endl;
std::cout<<"还剩下"<< sizeof...(args)<<"个参数"<<std::endl;
print(args...);//递归调用 继续展开参数
}
int main() {
print("西施", 18, "你好");
}
需求:
给出表白对象,但是表白前要喊一句口号
//递归中止时调用非模板函数,函数名要和展开参数包的递归函数相同 无参数
void print(){}
//第一个普通参数 第二个可变参数
template<typename T, typename ...Args>
void print(T arg, Args... args) {//arg本次参数 args尚未展开的参数
std::cout<<"亲爱的"<<arg<<",我要向你表白!"<< std::endl;
print(args...);//递归调用 继续展开参数
}
template<typename ...Args>
void biaoBai(const std::string&str,Args...args){
std::cout<<str<<std::endl;
print(args...);
std::cout<<"表白完成"<<std::endl;
}
int main() {
biaoBai("我是一个大帅锅", 18, "你好");
}
时间操作 chrono 库
C++11提供了 chrono 模板库,实现了一系列时间相关的操作(时间长度、系统时间、计时器)
头文件#include<chrono>
命名空间std::chrono
时间长度
duration
模板类用于表示一段时间(时间长度、时钟周期),如1小时、8分钟、5秒
常用的时间:
std::chrono::hours t1(1);//1小时
std::chrono::minutes t2(60);//60分钟
std::cout<<(t1==t2)<< std::endl;
std::cout<<t1.count()<<std::endl;//时钟周期的值
std::cout<<t2.count()<<std::endl;//时钟周期的值
duration
模板类重载了各种算术运算符,用于操作duration
对象
duration
模板类提供count方法,获取duration
对象的值
系统时间
system_clock
类支持了对系统时钟的访问
//引入
//#include <chrono>
//#include <iomanip>
//1.获取系统时间(C++时间)
//std::chrono::time_point<std::chrono::system_clock> now
auto now = std::chrono::system_clock::now();
//2.把系统时间转化为time_t时间(UTC时间)
time_t t_now = std::chrono::system_clock::to_time_t(now);
// 这两个之间可以进行时间偏移
//3.把time_t时间转化为本地时间(北京时间)
// localtime()不是线程安全的,VS用localtime_s代替,Linux用localtime_r代替
tm*tm_now = localtime(&t_now);
//4.格式化输出tm结构体的成员
std::cout<<std::put_time(tm_now,"%Y-%m-%d %H:%M:%S")<<std::endl;
std::cout<<std::put_time(tm_now,"%Y%m%d %H%M%S")<<std::endl;
//将时间写入字符串
std::stringstream ss;//创建stringstream对象ss 包含<sstream>头文件
ss<<std::put_time(tm_now,"%Y-%m-%d %H:%M:%S");//将时间输出到对象ss
std::string timestr = ss.str();//把ss对象转化为string对象
std::cout<<timestr<<std::endl;
计时器
steady_clock
类相当于秒表。操作系统只要启动就好进行时间的累加,常用于耗时的统计
std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
for (int i = 0; i < 100000; ++i) {
}
std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
auto dt = end - now;
std::cout << "耗时:" << dt.count() << "纳秒" <<"--"<<(double)dt.count()/(1000*1000*1000)<<"秒"<< std::endl;