zoukankan      html  css  js  c++  java
  • 【现代程序设计】【homework-07】

    C++11 中值得关注的几大变化

    1.Lambda 表达式

    Lambda表达式来源于函数式编程,说白就了就是在使用的地方定义函数,有的语言叫“闭包”,如果 lambda 函数没有传回值(例如 void ),其回返类型可被完全忽略。 定义在与 lambda 函数相同作用域的变量参考也可以被使用。这种的变量集合一般被称作 closure(闭包)。表达式的简单语法如下

    char s[]="Hello World!";  
      int Uppercase = 0; //modified by the lambda   
     for_each(s, s+sizeof(s), [&Uppercase] (char c) 
    {   
      if (isupper(c))    
      Uppercase++;    
     }); 
     cout << Uppercase << " uppercase letters in: " << s <<endl; 
    View Code

    在传统的STL中for_each()
    这个玩意最后那个参数需要一个“函数对象”,所谓函数对象,其实是一个class,这个class重载了operator(),于是这个对象可以像函数的式样的使用。实现一个函数对象并不容易,需要使用template,比如下面这个例子就是函数对象的简单例子(实际的实现远比这个复杂):

    template <class T> 
    class less { 
    public:     bool operator()(const T&l, const T&r)const  
      {     
        return l < r;    
     } 
    };
    View Code

    所以,C++引入Lambda的最主要原因就是1)可以定义匿名函数,2)编译器会把其转成函数对象。相信你会和我一样,会疑问为什么以前STL中的ptr_fun()这个函数对象不能用?(ptr_fun()就是把一个自然函数转成函数对象的)。原因是,ptr_fun() 的局限是其接收的自然函数只能有1或2个参数。

    那么,除了方便外,为什么一定要使用Lambda呢?它比传统的函数或是函数对象有什么好处呢?我个人所理解的是,这种函数之年以叫“闭包”,就是因为其限制了别人的访问,更私有。也可以认为他是一次性的方法。Lambda表达式应该是简洁的,极私有的,为了更易的代码和更方便的编程。

     

    2.自动类型推导 auto

    在在这一节中,原文主要介绍了两个关键字 auto 和 deltype,示例如下

     

    auto x=0; //x has type int because 0 is int
    auto c='a'; //char
    auto d=0.5; //double
    auto national_debt=14400000000000LL;//long long
     

    auto 最大的好处就是让代码简洁,尤其是那些模板类的声明,比如:STL中的容器的迭代子类型。

    vector<int>::const_iterator ci = vi.begin();

    可以变成:

    auto ci = vi.begin();
     

    模板这个特性让C++的代码变得很难读,不信你可以看看STL的源码,那是一个乱啊。使用auto必需一个初始化值,编译器可以通过这个初始化值推导出类型。因为auto是来简化模板类引入的代码难读的问题,如上面的示例,iteration这种类型就最适合用auto的,但是,我们不应该把其滥用。

    比如下面的代码的可读性就降低了。因为,我不知道ProcessData返回什么?int? bool? 还是对象?或是别的什么?这让你后面的程序不知道怎么做。

     

    auto obj = ProcessData(someVariables);
     

    但是下面的程序就没有问题,因为pObject的型别在后面的new中有了。

     

    auto pObject = new SomeType<OtherType>::SomeOtherType();

     

     

    3.自动化推导 decltype

     

    关于 decltype 是一个操作符,其可以评估括号内表达式的类型,其规则如下:

    1. 如果表达式e是一个变量,那么就是这个变量的类型。
    2. 如果表达式e是一个函数,那么就是这个函数返回值的类型。
    3. 如果不符合1和2,如果e是左值,类型为T,那么decltype(e)是T&;如果是右值,则是T。

    原文给出的示例如下,我们可以看到,这个让的确我们的定义变量省了很多事。

    1
    2
    3
    const vector<int> vi;
    typedef decltype (vi.begin()) CIT;
    CIT another_const_iterator;

    还有一个适合的用法是用来typedef函数指针,也会省很多事。比如:

    decltype(&myfunc) pfunc = 0;
      
    typedef decltype(&A::func1) type;
     
     
    4.auto 和 decltype 的差别和关系

    Wikipedia 上是这么说的(关于decltype的规则见上)

    #include <vector>
      
    int main()
    {
        const std::vector<int> v(1);
        auto a = v[0];        // a 的类型是 int
        decltype(v[0]) b = 1; // b 的类型是 const int&, 因为函数的返回类型是
                              // std::vector<int>::operator[](size_type) const
        auto c = 0;           // c 的类型是 int
        auto d = c;           // d 的类型是 int
        decltype(c) e;        // e 的类型是 int, 因为 c 的类型是int
        decltype((c)) f = c;  // f 的类型是 int&, 因为 (c) 是左值
        decltype(0) g;        // g 的类型是 int, 因为 0 是右值
    }
    如果auto 和 decltype 在一起使用会是什么样子?能看下面的示例,下面这个示例也是引入decltype的一个原因——让C++有能力写一个 “ forwarding
    function
     模板”,
     
    template< typename LHS, typename RHS>
      auto AddingFunc(const LHS &lhs, const RHS &rhs) -> decltype(lhs+rhs)
    {return lhs + rhs;}
     
    这个函数模板看起来相当费解,其用到了auto 和 decltype
    来扩展了已有的模板技术的不足。怎么个不足呢?在上例中,我不知道AddingFunc会接收什么样类型的对象,这两个对象的 +
    操作符返回的类型也不知道,老的模板函数无法定义AddingFunc返回值和这两个对象相加后的返回值匹配,所以,你可以使用上述的这种定义。
     
     
     
    5.统一的初始化语法

    C/C++的初始化的方法比较,C++ 11 用大括号统一了这些初始化的方法。

    比如:POD的类型。

    1
    2
    int arr[4]={0,1,2,3};
    struct tm today={0};

    关于POD相说两句,所谓POD就是Plain Old Data,当class/struct是极简的(trivial)、属于标准布局(standard-layout),以及他的所有非静态(non-static)成员都是POD时,会被视为POD。如:

    1
    2
    3
    struct A { int m; }; // POD
    struct B { ~B(); int m; }; // non-POD, compiler generated default ctor
    struct C { C() : m() {}; ~C(); int m; }; // non-POD, default-initialising m

    POD的初始化有点怪,比如上例,new A; 和new A(); 是不一样的,对于其内部的m,前者没有被初始化,后者被初始化了(不同 的编译器行为不一样,VC++和GCC不一样)。而非POD的初始化,则都会被初始化。

    从这点可以看出,C/C++的初始化问题很奇怪,所以,在C++ 2011版中就做了统一。原文作者给出了如下的示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    C c {0,0}; //C++11 only. 相当于: C c(0,0);
      
    int* a = new int[3] { 1, 2, 0 }; /C++11 only
      
    class X {
        int a[4];
        public:
            X() : a{1,2,3,4} {} //C++11, member array initializer
    };

    容器的初始化:

    1
    2
    3
    4
    5
    // C++11 container initializer
    vector<string> vs={ "first", "second", "third"};
    map singers =
    { {"Lady Gaga", "+1 (212) 555-7890"},
    {"Beyonce Knowles", "+1 (212) 555-0987"}};

    还支持像Java一样的成员初始化:

    1
    2
    3
    4
    5
    6
    class C
    {
       int a=7; //C++11 only
     public:
       C();
    };
  • 相关阅读:
    Java基础知识强化之集合框架笔记48:产生10个1~20之间的随机数(要求:随机数不能重复) 简洁版
    Java基础知识强化之集合框架笔记47:Set集合之TreeSet保证元素唯一性和比较器排序的原理及代码实现(比较器排序:Comparator)
    Java基础知识强化之集合框架笔记46:Set集合之TreeSet存储自定义对象并遍历练习2(自然排序:Comparable)
    Java基础知识强化之集合框架笔记45:Set集合之TreeSet存储自定义对象并遍历练习1(自然排序:Comparable)
    Java基础知识强化之集合框架笔记44:Set集合之TreeSet保证元素唯一性和自然排序的原理和图解
    docker学习整理
    python爬虫入门
    h3c 交换机配置VLAN和远程管理
    linux 使用串口连接设备console
    走势部署
  • 原文地址:https://www.cnblogs.com/lightz/p/3417006.html
Copyright © 2011-2022 走看看