C++11

文章目录

  • long long 类型
  • 二、char16_t和 char32_t类型
  • 原始字面量
  • 统一的初始化
  • 自动推导类型
  • 函数模板高级
    • decltype关键字
    • 函数后置返回类型
  • 模板的别名
  • 空指针nullptr
  • 智能指针
  • explicit关键字
  • 类内成员初始化
  • 基于范围的for循环
  • 新的STL容器
  • 嵌套模板尖括号
  • finial关键字
  • override关键字
  • 数值类型和字符串之间的转化
  • 静态断言
  • 常量表达式constexpr关键字
  • 默认函数控制
  • 委托构造和继承构造
    • 委托构造
    • 继承构造
  • lambda 函数
  • 右值引用
    • 左值、右值
    • 左值引用、右值引用
  • 移动语义
  • 完美转发
  • 可变参数模板
  • 时间操作 chrono 库
    • 时间长度
    • 系统时间
    • 计时器
  • C++11线程
  • 后续还会出分篇

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};

自动推导类型

在C和C++98中,auto 关键字用于修饰变量(自动存储的局部变量)
在 C++11 中,赋予了 auto 全新的含义,不再用于修饰的变量,而是作为一个类型指示符,指示编译器在编译时推导 auto 声明的变量的数据类型。

    auto a = 50;
    auto b = "你好";

注意

  • 使用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是一个占位符

模板的别名

C++11_第1张图片

空指针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 哈希表

嵌套模板尖括号

C++11_第2张图片

finial关键字

final 关键字用于限制某个类不能被继承,或者某个虚函数不能被重写

final 关键字放在类名或虚函数名的后面

override关键字

在派生类中,把 override 放在成员函数的后面,表示重写基类的虚函数,提高代码的可读性

在派生类中,如果某成员函数不是重写基类的虚函数,随意的加上 override 关键字,编译器会报错

数值类型和字符串之间的转化

数值到字符串
C++11_第3张图片
字符串到数值
C++11_第4张图片

静态断言

静态断言

常量表达式constexpr关键字

const既可以表示只读,也可以表示常量

C++11 标准为了解决 const 关键字的双重语义问题,保留了 const 表示“只读”的语义,而将“常量”的语义划分给了新添加的 constexpr 关键字

所以,C++11 标准中,建议将const和 constexpr 的功能区分开,表达“只读”语义的场景用 const,表达“常量”语义的场景用 constexpr

默认函数控制

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表达式或匿名函数

特点:距离近、简洁、高效和功能强大

语法:[](形参)->返回类型{}
C++11_第5张图片

  • 没有参数列表,括号可以不写
  • 不能有默认参数,不支持可变参数,所有的参数必须有参数名
  • 不写返回类型,自动推断,但是推荐显式的指定

捕获列表
通过捕获列表,lambda函数可以访问父作用域中的非静态局部变量(静态局部变量可以直接访问,不能访问全局变量)

捕获列表书写在[]中,与函数参数传递类似(原理),捕获的方式可以是值和引用
C++11_第6张图片
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(参数),用于转发参数,右值还是右值,左值还是左值
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
命名空间std::chrono

时间长度

duration模板类用于表示一段时间(时间长度、时钟周期),如1小时、8分钟、5秒
C++11_第7张图片

常用的时间:
C++11_第8张图片


    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 
//#include 
    //1.获取系统时间(C++时间)
    //std::chrono::time_point 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 包含头文件
    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;

C++11线程

后续还会出分篇

你可能感兴趣的:(C++,c++,java,开发语言)