c++11 新特性

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

《c++Primer》第五版 推荐序3

c++11标准对于语言核心部分做了相当大的改动。主要目的有若干个

  1. 强化静态类型推导比如c++11标准变更了auto关键字的语义引入了decltype关键字等这些措施利用了既存的变量和函数返回值的类型一方面增加了程序设计的弹性一方面避免了书写不必要的类型防止可能的错误而引入了constexpr关键字则进一步地将常量性的范围从单个变量扩展到了单次运算这将使得一大批既有的代码通过简单的修改而带来可观的编译期优化
  2. 支持函数式程序设计的语法比如引入了lambd表达式、引入了尾式函数申明语法、增加了for语句的冒号语法进行指定范围遍历等这些使得从函数式语言切换过来的程序员能够很容易地习惯c++这门新语言也给予了c++作为第一个门或唯一工作语言来学习的程序员以全新的方式书写原本易错的复杂申明和返回赋值的机会
  3. 将构造、析构和赋值过程中的可能错误加以防范尤其是临时对象生命周期相关的错误为此c++11标准引入了右值引用&&饰词、默认和禁用构造函数等
  4. 增加了对于面向对象范型中的一些一直未涵盖之内容的补充如允许继承而来的构造函数、引入表达禁止继承的final关键字、引入override关键字来支持派生类函数重写等。

当然c++11对于标准库、STL和泛型的扩充也绝对不可小觑但这些变更主要是为了配合语言核心的变更。比如为支持右值引用带来的对象所有权的流转引入了move算法--这在数学意义上也是对于代数完备性的一个有力补充更不用说由此带来的可观存储效率的提升了。

还有新引入的三种智能指针和四种无序关联容器、字符串和数值类型互相转换的工具函数以及新引入的若干针对标准容器的小改进如顺序容器的常量起始和终止迭代器以及可以直接插入值而不必再构造临时变量的emplace函数族等等。

c++11 的新特性

2.1.1 long long 类型

        page:31
        c++ 11重新定义了long long 类型必须大于等于64bit。 这似乎看起来并不是一个什么新的特性因为c++11之前各个平台的编译器也自己实现了类型 long long只是没有明确的标准来统一。所以这个特性对于我们写代码来说不会有太多区别。


2.2.1 列表初始化page39


        《c++程序设计语言》 6.3.5
        初始化器就是对象在初始化状态下被赋予的值。初始化器有4中可能的形式

X a1 {v};
X a2 = {v};
X a3 = v;
X a4(v);

在这些形式中只有第一种不受任何限制在所有场景中都能使用。我强烈建议程序员使用这种方式为变量赋初值它含义清晰与其他形式相比不太容易出错。不过第一种初值形式a1在c++11新标准中刚刚被踢出因此在老代码中使用的都是后面三种形式。其中使用=的两种形式是从c语言继承而来的。使用{}的初始化称为列表初始化list initialization,它能防止窄化转换。
demo

#include<iostream>
int main(){
    // 1.0 列表初始化内置类型提供默认值
    char *p {}; // 默认值是 nullptr
    long long a = {}; // 默认0
    bool bl ={}; // 默认false

    // 2.0 将会是未知数值。
    long long b;

    // 3.0 窄化转换
    double db {12.123};
    int db2 {db};// 编译器可以检测出错误警告。发生截断窄化转换
    int db3 = db; // 编译器没有提示发生窄化转换

    
    std::cout<<sizeof (long long)<<std::endl;
    std::cout<<"a "<<a<<std::endl;
    std::cout<<"b "<<b<<std::endl;
    std::cout<<std::boolalpha<<"bl "<<bl<<std::endl;
    std::cout<<" p ==nullptr "<<(p==nullptr)<<std::endl;
    std::cout<<"db2 "<<db2<<std::endl;
    std::cout<<"db3 "<<db3<<std::endl;
}


can@ubuntu:~/work/c++$ g++ -std=c++11 11.cpp -Wall
11.cpp: In function ‘int main()’:
11.cpp:12:14: warning: narrowing conversion of ‘db’ from ‘double’ to ‘int’ [-Wnarrowing]
   12 |     int db2 {db};// 编译器可以检测出错误警告。发生截断窄化转换
      |              ^~
11.cpp:16:22: warning: ‘b’ is used uninitialized in this function [-Wuninitialized]
   16 |     std::cout<<"b "<<b<<std::endl;
      |                      ^
can@ubuntu:~/work/c++$ ./a.out
8
a 0
b 0
bl false
 p ==nullptrtrue
db2 12
db3 12

当我们使用auto 关键字从初始化器推断变量的类型时没必要采用列表初始化的方式。而且如果初始化器是{}列表则推断得到的数据类型肯定不是我们想要的结构。当使用auto的时候应该选择=的初始化形式。

auto z1{99}; // z1的类型时 initizlizer_list<int>

auto z2 = 99; // z2的类型时 int
 

 当我们使用{}符号进行初始化时不会进行窄化转换实际操作在g++中是编译器给出警告在使用auto的语句中{}列表初始化的类型被推断为std::initializer_list<T>.

2.3.2 nullptr 常量

          NULL  是一个宏定义预处理为 0 。 这是一个整数所以在函数调用过程中如果作为参数的化可能产生二义性NULL 作为实参就可能被当做实参类型为int而nullptr则不会nullptr的类型就是一个指针。
        nullptr 为一个常量代表空指针。  
        所以尽量使用nullptr来作为空指针。


2.4.4 constexpr变量常量表达式
6.5.2 constexpr函数
7.5.6 constexpr构造函数     

         constexpr变量
        定义常量表达式是指值不会改变并且在编译过程就能得到计算结果的表达式。
        用法c++11 新标准规定允许将变量申明为constexpr类型以便由编译器来验证变量的值是否是一个常量表达式。 明确指出程序员的意图让编译器来检测确保它一定是一个常量值
        建议一般来说如果你认定变量是一个常量表达式那就把它声明成 constexpr类型。
        constexpr函数
        定义constexpr函数是指能用于常量表达式的函数。
                定义constexptr函数的方法与其他函数类似不过要遵循几项约定函数的返回类型及所有形参的类型都是字面值类型而且函数体中必须有且只有一条return语句
        constexpr int new_sz() { reutrn 42;}
        constexpr int foo = new_sz(); // 正确foo是一个常量表达式。
执行该任务时编译器把对constexpr函数的调用替换成结果值。为了能在编译过程中随时展开constexpr函数被隐式地制定为内敛函数。
        内敛函数和constexpr函数可以再程序中定义多次。毕竟编译器想要展开函数仅有函数申明是不够的还需要函数的定义。不过对于某个给定的内敛函数或者constexptr函数来说它的定义必须完全一致。基于这个原因内敛函数和constexprhan函数通常定义在头文件中。
        constexpr函数不一定返回常量表达式

 

#include<iostream>

constexpr size_t scale(size_t cnt){return 12*cnt;}

int main(){
    int arr[scale(2)];
    int i =2;
    // 需要编译阶段确定scale(i)的值所以这里是错误的但是gcc 并没有报错也没有警告 什么情况
    int a2[scale(i)];

//运行阶段确定 scale(i)的值。
    std::cout<<scale(i)<<std::endl;

    std::cout<<"arr: "<<sizeof(arr)/sizeof(arr[0])<<std::endl;
    std::cout<<"a2: "<<sizeof(a2)/sizeof(a2)<<std::endl;

    // 结论。 constpexpr 并不是一定在编译阶段确定返回值。
}

can@ubuntu:~/work/c++$ g++ -std=c++11 constexpr.cpp -Wall
can@ubuntu:~/work/c++$ ./a.out
24
arr: 24
a2: 1

can@ubuntu:~/work/c++$ g++ --version
g++ (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

书《c++程序设计语言》2.2.3

  • cosnt :大致意思是“我承诺不改变这个值”。主要用于说明接口这样在把变量传入函数时就不必担心变量会在函数内被改变了。编译器负责确认并执行const的承诺。
  • constexpr大致意思是在编译时求值。主要用于说明常量作用是允许将数据置于只读内存中不太可能被破坏以及提升性能。

        constexpr函数可以接受非常量实参但此时结果将不会是一个常量表达式。当程序的上下文不需要常量表达式时我们可以使用非常量表达式实参来调用constexpr函数这样我们就不用把同一个函数定义两次了其中一个用于常量表达式一个用于变量。

个人总结 对于constexpr 变量编译器会检查确保这个值能在编译期间确定值。  但是如果定义了一个constexpr变量想让这个变量是通过一个函数计算得到的那么被使用的这个函数就必须是constexpr函数。  constexpr函数并不会要求它的计算结果是一定在编译阶段确定的它可以复用在用于计算constexpr变量时编译器会检查计算结果确保是编译期间确定在用于普通的运行时确定值时它一样遵循普通的函数调用在运行时确定返回值。所以constexpr函数的“constexpr特性”只是在被constexpr变量使用时才体现。 这也就是 constexpr函数是指能用于常量表达式的函数 这句话的意思。

constexpr 构造函数 
        7.5.6 尽管构造函数不能是const的但是字面常量类的构造函数可以是constexptr函数。constexpr构造函数可以声明成=default的形式或者是删除的形式。否则constexpr构造函数就必须既符合构造函数的要求意味着不能包含返回语句又符合constexpr函数的要求意味着它能拥有的唯一可执行语句就是返回语句综合这两点 (很矛盾所以应该说constexptr构造函数是个特例)constexpr构造函数体一般来说应该是空的。constexpr构造函数必须初始化所有数据成员初始值只能用constexpr构造函数或者常量表达式。

2.5.1 类型别名

        传统的方法使用typdef新标准使用using

        using  MY_DATA = struct _data; // 

2.5.3 decltype 

        decltype(f()) sum = x; // sum的类型就是函数f的返回类型

        int i =0;

        decltype((i)) d = i; // decltype的表达式如果是加上了括号的变量结果将是引用 d是int&,必须初始化

2.6 类内初始化

        c++ 11新标准规定可以为数据成员提供一个类内初始值。创建对象时类内初始值将用于初始化数据成员。
       class Dog{
        private: int age = 1;
        }

3.2.3 范围for语句

//输出 str中的每一个字符   如果要修改其中的字符则 应该 在for里面使用 auto &c引用类型。
        string str("some string");
        for( auto  c : str){
             cout << c << endl;
        }

6.1.4 使用=default 生成默认构造函数

如果我们定义了带参数的构造函数编译器就不会自动给类定义默认的无参构造函数如果代码中又确实需要无参的默认构造函数这个时候可以用 =default 让编译器生成默认构造函数非常方便。


7.5.2 委托构造函数  

class Sales_data{
    public: 
       // 非委托构造汉纳树使用对应的实参初始化成员
        Sales_data( std::string s, unsigend cnt, double price):
            bookNo(s), units_sold(cnt), revence(cnt*price){
        }
        
        // 其余构造函数全部委托给另一个构造函数
    Sales_data(): Sales_data("", 0, 0){}; // 默认构造函数被委托给了第一个三参数函数
    Sales_data(std::string s): Sales_data(s,0,0){};// 

    Sales_data(std:::istream &is) Sales_data(){ //该函数委托给了默认构造函数而默认构造函数再委托给三参数函数
        read(is, *this);
    }
}

9.1 forward_list 单向联表array 固定大小数组。

10.3.2 lambda 表达式

 可调用对象
        到目前为止我们使用过的仅有的两种可调用对象时函数和函数指针。还有其他两种可调用对象重载了函数调用运算符的类以及lambda表达式。
lambda 表达式表示一个可调用的代码单元。lamba具有一个返回类型一个参数列表和一个函数体与函数不同的是lambda可能定义再函数内部。
        auto compare = [=](const string &s){ return s.size() >= sz; };
find_if(words.begin(),words.end(),compare);

10.3.4 标准库bind函数

         函数适配器特点就是可以再调用的时候一次绑定参数关系可以事先绑定固定的参数也可以重排原来函数的参数顺序。
        auto newCallable = bind(callable, arg_list);
其中newCallable本身是一个可调用对象arg_list 是一个逗号分隔的参数列表对应给定的callable的参数。即当我们调用newCallable时newCallable会调用callable,并传递给它arg_list中的参数。

12.1 智能指针

        shared_ptr<vector <int>> v = make_shared<vector<int>>();
        unique_ptr<vector <int>> v2 = make_unique<vector<int>>();

15.2.2 虚函数override指示符

class Animal{
public : 
    Animal() = default;
    virtual ~Animal() = default;
    virtual void eat() = 0;
};

class Dog : public Animal{
public:
    void eat() override{
        std::cout<<"dog eat"<<std::endl;
    }
};

.........其他。。。。。。。。。。。。。。

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: c++