登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

守望

路越远,心越近。有多远,走多远!

 
 
 

日志

 
 

C++11的语法新特性  

2011-07-03 00:06:07|  分类: 编程 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

本文的主要内容来源:
http://www.softwarequalityconnection.com/2011/06/
        the-biggest-changes-in-c11-and-why-you-should-care/


从C++出来到现在已经13年了。
Bjarne Stroustrup(C++的创造者)最近评价C++11:”感觉像个新的语言“。
事实上,C++11核心已经发生了很重大的变化:
1. 支持Lambda表达式( lambda expressions)
2. 对象自动类型推导(automatic type deduction of objects)
3. 统一初始化语法(uniform initialization syntax)
4. 代理构造(delegating constructors)
5. deleted 和defaulted函数声明(deleted and defaulted function declarations)
6. 空指针(nullptr
7. 右值引用(rvalue references)
8. ...

C++11的标准库也已经修订,使用了新的算法,新的容器类,原子操作,类型,
正则表达式,智能指针, async() 功能,及多线程库。

关于C++11核心和库的新特性的完整描述,请见以下网址:
http://www2.research.att.com/~bs/C++0xFAQ.html

1. Lambda表达式(Lambda Expressions)

    Lambda表达式允许你在本地定义函数,即在调用的地方定义,

    从而消除函数对象产生的许多安全风险,Lambda表达式的格式如下:

            [capture](parameters)->return-type {body} 

            []里是函数调用的参数列表,表示一个Lambda表达式的开始,

         让我们来看一个Lambda例子:

         假 设你想计算某个字符串包含多少个大写字母,使用for_each()遍历一个char数组,

         下面的Lambda表达式确定每个字母是否是大写字母,每当它 发现一个大写字母,

         Lambda表达式给Uppercase加1,Uppercase是定义在Lambda表达式外的一个变量:

          int main() 
         
{
           
   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;
          

          以上例子就好像你在一个函数调用内部定义了一个新的函数。[&Uppercase]中的“&”记号

          意味着Lambda主体获得一个 Uppercase的引用,以便它能修改,如果没有这个特殊记号,

          Uppercase将通过值传递,C++11 Lambda表达式也包括成员函数构造器。


2. 自动类型推导和声明类型(decltype)

    在C++03中,在声明对象时,你必须指定对象的类型,然而,在许多情况下,

    对象声明时都有初始化,C++11利用了这个优势,允许你声明对象时不指定类型:

        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
    自动类型推导主要用于对象类型很长很麻烦的时候,或者是对象是自动生成的
    时候(使用模板时)

    考虑下面迭代器的声明:

        void fucn(const vector<int> &vi) 
      
{
          
vector<int>::const_iterator ci=vi.begin();
      
}
   有了自动类型推导后,你可以这样声明:       

       auto ci=vi.begin();      //  哈哈,省事了

    关 键字auto不是什么新生事物,我们早已认识,它实际上可以追溯到前ANSI C时代,

    但是,C++11改变了它的含义,auto不再指定自动存储类型对象,相反,它声明的对象

    类型是根据初始化代码推断而来的,C++11删除了 auto关键字的旧有含义以避免混淆。

    注意了:auto已经不再是当年的auto了!

    C++11提供了一个类似的机制捕捉对象或表达式的类型,新的操作符decltype需要一个

    表达式,并返回它的类 型。

        const vector<int> vi;         typedef decltype (vi.begin()) CIT;         CIT another_const_iterator;
3. 统一初始化语法(uniform initialization syntax)

    C++至少有4个不同的初始化符号,有些存在重叠,

    括号初始化语法如下:

        std::string s("hello");
       
int m=int();           //default initialization  

    在某些情况下,你也可以使用“=”符号进行初始化:

        std::string s="hello";
       
int x=5;  

    对于POD聚合,你还可以使用大括号:

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

    最后,构造函数使用成员进行初始化:

        struct S { 
           
int x;  
            S(): x(0) {}
        };  

    显然,这么多种初始化方法会引起混乱,对新手来说就更痛苦了,更糟糕的是,

    在C++03中,你不能初始化POD数组成员,POD数组使用new[]分配,

    C++11使用统一的大括号符号清理了这一混乱局面。

        class C  {
            
int a;
            
int b; 
            
public:  
                C(int i, int j);
        
};  
       
C c {0,0}; //C++11 only. Equivalent to: 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
       
}; 

        对于容器,你可以和一长串的push_back()调用说再见了,在C++11中,

        你可以直观地初始化容器:

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

        类似地,C++11支持成员在类内初始化:

        class C  {  

            int a=7;    //C++11 only  这和Java一样

            public:  

            C(); 

        };

4. 代理构造(delegating constructors)

    在C++11中,构造函数可以调用类中的其它构造函数:

    class M //C++11 delegating constructors
   
{  
        int x, y;  

        char *p; 
       
public:  
            M(int v) : x(v), y(0),  p(new char [MAX])  {} //#1 target  

            M(): M(0) {cout<<"delegating ctor"<  

     构造函数#2,代理构造函数,调用目标构造函数#1。


5. deleted 和defaulted函数声明(deleted and defaulted function declarations)

    一个结构体中的函数:

    struct A  {  
        A()=default//C++11  

        virtual
 ~A()=default//C++11
 
   
};  

    对于被称为defaulted的函数,“=default;”部分告诉编译器为函数生成默认实现。

    Defaulted函数有两个好处:比手工实现更高效,让程序员摆脱了手工定义这些函数的麻烦事

    与defaulted函数相反的是deleted函数:

    int func()=delete;

    Deleted函数对防止对象复制很有用,回想一下C++自动为类声明一个拷贝构造函数和一个赋值操作符,

    要禁用拷贝,声明这两个特殊的成员函数为=delete即可:

    struct NoCopy  {    
        NoCopy & operator =( const NoCopy & ) = delete;  
  
        NoCopy ( const NoCopy & ) = delete

    
}; 
    
NoCopy a; 
    
NoCopy b(a);     //compilation error, copy ctor is deleted 


6. 空指针(nullptr

    终于,C++有了一个指定空指针常量的关键字,nullptr取代了有错误倾向的NULL宏和0,

    这两个空指针替代品已经使用很多年了,nullptr是一个强类型:

        void f(int);           //#1 
        void f(char *);     //#2
       
//C++03 
       
f(0);                    //which f is called?  0可能是int,也可能是空指针,调谁?
      
//C++11 
      
f(nullptr)            //unambiguous, calls #2  这下好了,空指针只能是nullptr。

    nullptr适用于所有指针类别,包括函数指针和成员指针: 

        const char *pc=str.c_str();      //data pointers
       
if (pc!=nullptr)  
            cout<<pc<<endl;
       
int (A::*pmf)()=nullptr;         //pointer to member function
       
void (*pmf)()=nullptr;          //pointer to function 

7. 右值引用(rvalue references)

    C++03中的引用类型只能绑定左值,C++11引入了一种新的引用类型,叫做右值引用,
    右值引用可以绑定右值,例如,临时对象和字符串。
    增加右值 引用的主要原因是移动语义(move semantics),与传统的复制不一样,
    移动意味着目标对象偷窃了源对象的资源,    留下一个状态为“空”的源对象。
    在某些情况下,复制 一个对象既代价高又没有必要,这时可以用一个移动操作代替。
    如果你想评估移动语义(move semantics)带来的性能收益,可以考虑字符串交换,
    一个幼稚的实现如下:
        void
 naiveswap(string &a, string & b)  

、    {  

            string temp = a;  

            a=b;  

            b=temp; 
       
}  

    这样的代价很高,复制字符串必须分配原始内存,将字符从源拷贝到目标。

    相反,移动字符串仅仅是交换两个数据成员,不用分配内存,拷贝char数组和删除内存:

        void moveswapstr(string& empty, string & filled)  

        {  

           //pseudo code, but you get the idea  

           size_t sz=empty.size();  

           const char *p= empty.data();  

           //move filled's resources to empty  

           empty.setsize(filled.size());  

           empty.setdata(filled.data());  

           //filled becomes empty  

           filled.setsize(sz);  

           filled.setdata(p);  

        }  

    如果你实现的类支持移动,你可以像以下那样声明一个移动构造函数和一个移动赋值操作符:

        class Movable  
        { 
           
Movable (Movable&&); //move constructor 
           
Movable&& operator=(Movable&&); //move assignment operator
       
};

    C++11标准库广泛的使用了移动语义,许多算法和容器都为移动做了优化。

  评论这张
 
阅读(13297)| 评论(4)

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018