关闭

C++11简介(五)

63人阅读 评论(0) 收藏 举报
分类:

5.3 类型推导
     在C++03(还有C)中,必须显式指定变量的类型.然而,随着模板类型和模板元编程技术的出现,某些东西的类型,尤其是函数的返回类型,可能不是那么容易表示的了. 在这种情况下,将中间结果存储在变量中是件困难的事情.可能需要去了解特定的模板元编程库的内部实现.
    C++11提供两种方法来缓解上述问题.一,定义有显式初始化的变量可以用auto关键字来自动确定变量类型,这将用初始化表达式的类型来创建变量:
        auto some_strange_callable_type = boost::bind(&some_function, _2, _1, some_object);
        auto other_variable = 5;
    some_strange_callable_type的类型很简单, 就是boost::bind模板函数返回值的类型.作为编译器语义分析责任的一部份,编译很容易确定这个类型,但用户就没那么容易确定了.
    otherVariable 的类型同样也是定义明确的,但用户很容易就能判别。它是个 int(整数),就和整数字面值的类型一样。
    另外,关键字decltype可以用来在编译期确定表达式的类型.例如
        int some_int;
        decltype(some_int) other_integer_variable = 5;
    decltype 和 auto 一起使用会更为有用,因为 auto 参数的类型只有编译器知道.然而 decltype对于那些大量运用运算符重载和类型特化来编码的表达式非常有用。
    auto对减少代码冗余也很有用.比如说, 程序员不用像下面这样写代码:
        for (std::vector::const_iterator itr = myvec.cbegin(); itr != myvec.cend(); ++itr)
    而可以用更简短的形式:
        for (auto itr = myvec.cbegin(); itr != myvec.cend(); ++itr)
    这两种形式的差距会随着你使用嵌套容器而增加, 这种情况下typedef也是一种减少代码的好方法!由decltype得出的类型可以和由auto推导出的类型不同:
   #include
   int main() {
   const std::vector v(1);
   auto a = v[0];        // a 是 int 类型
   decltype(v[1]) b = 1; // b 是 const int& 类型, 是std::vector::operator[](size_type) const

                         // 的返回类型

   auto c = 0;           // c 是 int 类型
   auto d = c;           // 是 int 类型
   decltype(c) e;        // e 是 int 类型, c变量的类型
   decltype((c)) f = c;  // f 是int&类型, 因为(c)是一个左值

   decltype(0) g;        // g 是 int 类型, 因为0是一个右值

  }

5.4 基于范围的for循环
    在C++03中,要遍历一个list中的元素需要很多代码.其它语言实现支持"糖块句法",允许程序通过一个简单的"foreach"语句自动遍历list中的元素.其中之一就是Java语言, 它从5.0开始支持增强的for循环.
    C++11增加了一个类似的特性, for语句可以简单地遍历列表中的元素.
        int my_array[5] = {1, 2, 3, 4, 5};
        // double the value of each element in my_array:
        for (int &x : my_array) {
        x *= 2;
        }
    这种形式的for语句叫作"基于范围的for语句",它会遍历列表中的每一个元素.可以用在C风格数组,初始化列表和那些带有能返回迭代器的begin()和end()函数的类型上.所有提供了begin/end的标准容器都可以使用基于范围的for语句.

5.5 Lamda函数与表达式
    C++11提供了创建匿名函数的能力,叫做Lamda函数. 具体内容请参考: http://www.cnblogs.com/pzhfei/archive/2013/01/14/lambda_expression.html
 
5.6 函数语法的新选择

    标准C的函数声明语法对C语言的特性集而言完全足够了. 因为C++从C发展而来, 保留了C的基本语法并在需要的地方进行了扩展. 然而,C++的结构变得更加复杂了,暴露出了很多的局限性,尤其是模板函数的声明.下面的例子在C++03中是不允许的:
        template
        Ret adding_func(const Lhs &lhs, const Rhs &rhs) {return lhs + rhs;} //Ret must be the type of lhs+rhs
    Ret的类型是lhs+rhs的结果的类型.即使用前面提到的C++11中的decltype,也是不行的:
        template
        decltype(lhs+rhs) adding_func(const Lhs &lhs, const Rhs &rhs) {return lhs + rhs;} //Not legal C++11
    这不是合法的C++,因为lhs和rhs还没定义;解析器解析完函数原型的剩余部分之前,它们还不是有效的标识符.
    为此, C++11引入了一种新的函数声明语法,后置返回类型(trailing-return-type).
        template
        auto adding_func(const Lhs &lhs, const Rhs &rhs) -> decltype(lhs+rhs) {return lhs + rhs;}
    这种语法可以用到更普通的函数声明和定义上:
        struct SomeStruct  {
            auto func_name(int x, int y) -> int;
        };
 
       auto SomeStruct::func_name(int x, int y) -> int {
       return x + y;
       }
    关键字auto的这种用法与在自动类型推导中有所不同.

5.7 对象构造的改进
    C++03中类的构造函数不允许调用该类的其它构造函数;每个构造函数都必须自己构造类的全部成员或都调用一个公共的成员函数.例如:
class SomeType  {
    int number;
 
public:
    SomeType(int new_number) : number(new_number) {}
    SomeType() : number(42) {}
};
class SomeType  {
    int number;
 
private:
    void Construct(int new_number) { number = new_number; }
public:
    SomeType(int new_number) { Construct(new_number); }
    SomeType() { Construct(42); }
};
    基类的构造函数不能直接暴露给派生类;每个派生类必须实现自己的构造函数哪怕基类的构造函数已经够用了.非静态数据成员不能在声明的地方初始化.它们只能在构造函数中初始化.
C++11为这些问题提供了解决方案.
C++11允许构造函数调用另一个构造函数(叫做委托构造).这允许构造函数利用其它构造函数的行为而只需增加少量的代码.C#,java和D语言都提供了这种功能. C++的语法如下:
class SomeType  {
    int number;
 
public:
    SomeType(int new_number) : number(new_number) {}
    SomeType() : SomeType(42) {}
};
    注意:这个例子可以通过给new_number设定一个默认参数来达到相同的效果.但是,这种新语法可以让这个默认值在实现中来设置而不是在接口中设置.这带来的一个好处就是,对库代码的维护者而言,在接口中(头文件中)声明默认值,这个默认值被嵌入到了调用端;要改变这个默认值的话,调用端的代码都需要重新编译.但委托构造可以在实现中(CPP文件中)来改变这个默认值, 这样调用端的代码就不需要重新编译,只用重新编译这个库就可以了.
    这有一个附加说明: C++03认为,构造函数执行完了一个对象就被构造好了. 而C++11则认为,只要任何一个构造函数执行完了,对象就算构造完成了. 由于可能有多构造函数会被执行,C++11的这种做法就意味着,所有的委托构造函数都将在一个已经用它自己类型完全构造好的对象上执行.这句话什么意思呢?举个例子, B 继承自 A, B的构造函数里调用了A的构造函数;当A的构造函数执行完以后,就已经有一个A类的对象构造完成了.而这时B的构造函数不会再构造一个新对象,而是把那个A对象改造成B类的对象(这是我的推测).再举一个例子,类C有两个构造函数C1和C2, C2调用了C1. 当C1执行完后,已构造好了一个C类对象.而这时C2的代码会直接作用在这个对象上,不会再构造一个新对象.C++03就会构造2个对象,其中一个是临时对象.
    对于基类的构造函数,C++11允许一个类指定要不要继承基类的构造函数.注意,这是一个"全部"或"全不"的特性,要么继承基类的全部构造函数,要么一个都不继承.
    此外,对多重继承有一些限制,从多个基类继承而来的构造函数不可以有相同的函数签名(signature).而派生类的新加入的构造函数也不可以和继承而来的基类构造函数有相同的函数签名,因为这相当于重复声明.
语法如下:
class BaseClass {
public:
    BaseClass(int value);
};
 
class DerivedClass : public BaseClass {
public:
    using BaseClass::BaseClass;
};
对于成员初始化,C++11允许下面这样的语法:
class SomeClass {
public:
    SomeClass() {}
    explicit SomeClass(int new_value) : value(new_value) {}
 
private:
    int value = 5;
};
任何一个构造函数都将把value初始化为5, 如果它们没用其它值来覆盖这个初始化的话.所上面那个空的构造函数会把value初始化为类定义时的状态5.而那带有参数的构造函数会用指定的值来初始化value.成员的初始化也要以使用前面提到的统一初始化.

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:6595次
    • 积分:144
    • 等级:
    • 排名:千里之外
    • 原创:3篇
    • 转载:34篇
    • 译文:0篇
    • 评论:1条
    最新评论