走进c++0x,体验不一样的c++

   好吧,之前我错误的认为c++0x就是添加了一些大多数人不懂的语法,比如bind,lambda,或者是一些使用起来可有可无的特性,比如auto,或者是可以通过具体代码方式来避免的,比如move。

        不过阅读了漫话c++0x系列文章,我发现c++0x真的是一门新的语言,并且是足够漂亮的新语言。说实话,我们平时写代码确实不会遇到什么复杂的语法(如果自己写了自以为很炫,但是别人都看不懂的语句,就自行面壁去)。c++一些容易产生bug或者是非常难以理解的东西应该是我们尽量去避免的(比如,多继承和模板),模板不是不可以用,而是应该限定到非常有限的地方。

        但是随着c++0x的普及(各主流编译器都已经支持),学习使用c++新语法就变得非常有必要了。通过下面介绍的一些特性,我们的代码可以变得更加简洁,从而可以使我们的代码更加容易阅读(无论是对他人而言,还是对自己而言)。

=============================分割线=================================

一、auto用来声明变量

      把这个列在第一位是因为这个是使用最广泛并且最容易被使用的。它最常见的地方就是容器遍历,请看下面的代码:

  1. // c++98 
  2. std::map< std::string, std::vector<int> > mp; 
  3. for (std::map< std::string, std::vector<int> >::iterator i = mp.begin(); i != mp.end(); ++i) 
  4.     /// traverse the map 
  5.  
  6. // c++0x 
  7. std::map< std::string, std::vector<int> > mp; 
  8. for (auto i = mp.begin(); i != mp.end(); ++i) 
  9.     /// traverse the map 
// c++98
std::map< std::string, std::vector<int> > mp;
for (std::map< std::string, std::vector<int> >::iterator i = mp.begin(); i != mp.end(); ++i)
{
    /// traverse the map
}

// c++0x
std::map< std::string, std::vector<int> > mp;
for (auto i = mp.begin(); i != mp.end(); ++i)
{
    /// traverse the map
}

二、for循环新的遍历方式(像java一样的遍历方式)

 

  1. // c++98 
  2. std::vector< int> v; 
  3. for (std::vector< int>::iterator i = v.begin(); i != v.end(); ++i) 
  4.     /// use iterator i to operate the element in v 
  5.  
  6. // c++0x 
  7. std::vector< int> v; 
  8. for (int num: v) /// for (auto num: v) is also OK 
  9.     /// num is the element in v 
  10.   
  11. std::vector< int> v; 
  12. for (int & num: v) /// for (auto & num: v)  is also OK 
  13.     /// num is the reference of the element in v 
// c++98
std::vector< int> v;
for (std::vector< int>::iterator i = v.begin(); i != v.end(); ++i)
{
    /// use iterator i to operate the element in v
}

// c++0x
std::vector< int> v;
for (int num: v) /// for (auto num: v) is also OK
{
    /// num is the element in v
}
 
std::vector< int> v;
for (int & num: v) /// for (auto & num: v)  is also OK
{
    /// num is the reference of the element in v
}

三、统一初始化的语法,C++0x中,所有的初始化都可以用{}来完成了,达到了真正意义上的统一,语法也十分简洁。这里有一点需要注意,在C++0x的的统一初始化逻辑中,参数的个数可以少于成员,但不能多于,如果少于的话,没初始化的成员通过缺省的方式初始化

 

  1. /// c++98中,各种形式的初始化 
  2. constint x(5);           ///< 直接初始化 
  3. constint y = 5;          ///< copy构造初始化 
  4. int arr[] = {1, 2, 3};    ///< 大括号初始化 
  5. struct Point{int x, y;}; 
  6. const Point p = {1, 2};   ///< 大括号初始化 
  7. class PointX 
  8. public
  9.     PointX(int x, int y); 
  10. private
  11.     int x, y; 
  12. }; 
  13. const PointX px(1, 2);    ///< 构造函数初始化 
  14. std::vector< int> vec(arr, arr+3); ///< 从别的容器初始化 
  15.  
  16. /// c++98中,不能初始化的情况 
  17. class Foo 
  18. public
  19.     Foo() : data(???) {} ///< 不能在这里初始化数组 
  20. private
  21.     int data[3]; 
  22. }; 
  23. int * data = newint[3]; ///< 不能在这里初始化堆上内存 
/// c++98中,各种形式的初始化
const int x(5);           ///< 直接初始化
const int y = 5;          ///< copy构造初始化
int arr[] = {1, 2, 3};    ///< 大括号初始化
struct Point{int x, y;};
const Point p = {1, 2};   ///< 大括号初始化
class PointX
{
public:
    PointX(int x, int y);
private:
    int x, y;
};
const PointX px(1, 2);    ///< 构造函数初始化
std::vector< int> vec(arr, arr+3); ///< 从别的容器初始化

/// c++98中,不能初始化的情况
class Foo
{
public:
    Foo() : data(???) {} ///< 不能在这里初始化数组
private:
    int data[3];
};
int * data = new int[3]; ///< 不能在这里初始化堆上内存
  1. /// 与上面c++98中能初始化的例子相对应 
  2. int x {1}; 
  3. int y {2}; 
  4. int arr[] {x, y, 3}; 
  5. struct Point {int x, y;}; 
  6. Point p {1, 2}; 
  7. class PointX 
  8. public
  9.     PointX(int x, int y); 
  10. private
  11.     int x, y; 
  12. }; 
  13. PointX px {1, 2}; 
  14. std::vector< int> vec {1, 2, 3}; 
  15.   
  16. /// 与上面C++98中不能初始化的例子相对应 
  17. class Foo 
  18. public
  19.     Foo() : data {1,2,3} {} 
  20. private
  21.     int data[3]; 
  22. }; 
  23. int * data = newint[3] {1, 2, 3}; 
/// 与上面c++98中能初始化的例子相对应
int x {1};
int y {2};
int arr[] {x, y, 3};
struct Point {int x, y;};
Point p {1, 2};
class PointX
{
public:
    PointX(int x, int y);
private:
    int x, y;
};
PointX px {1, 2};
std::vector< int> vec {1, 2, 3};
 
/// 与上面C++98中不能初始化的例子相对应
class Foo
{
public:
    Foo() : data {1,2,3} {}
private:
    int data[3];
};
int * data = new int[3] {1, 2, 3};

除了上面介绍这些,还有一些更酷的写法

 

  1. void Func(const std::vector< int>& v); 
  2. Func({1, 2, 3, 4, 5}); 
  3. Point MakePoint() { return { 0, 0 }; } 
void Func(const std::vector< int>& v);
Func({1, 2, 3, 4, 5});
Point MakePoint() { return { 0, 0 }; }

四、function    其实就是boost::function,最常见的用途就是实现函数回调,并且function是可以像对象一样被用作参数或是被保存到容器中,这个是相对大一些的内容,细节请自行google,简单示例如下

 

  1. #include < functional> 
  2.   
  3. std::function< size_t(constchar*)> print_func; 
  4.   
  5. /// normal function -> std::function object 
  6. size_t CPrint(constchar*) { ... } 
  7. print_func = CPrint; 
  8. print_func("hello world"): 
  9.   
  10. /// functor -> std::function object 
  11. class CxxPrint 
  12. public
  13.     size_t operator()(constchar*) { ... } 
  14. }; 
  15. CxxPrint p; 
  16. print_func = p; 
  17. print_func("hello world"); 
#include < functional>
 
std::function< size_t(const char*)> print_func;
 
/// normal function -> std::function object
size_t CPrint(const char*) { ... }
print_func = CPrint;
print_func("hello world"):
 
/// functor -> std::function object
class CxxPrint
{
public:
    size_t operator()(const char*) { ... }
};
CxxPrint p;
print_func = p;
print_func("hello world");

五、bind    其实就是boost::bind,一般和function配合起来使用,用来替代原来stl中丑陋的bind1st和bind2nd,示例如下

 

  1. #include < functional> 
  2.   
  3. int Func(int x, int y); 
  4. auto bf1 = std::bind(Func, 10, std::placeholders::_1); 
  5. bf1(20); ///< same as Func(10, 20) 
  6.   
  7. class
  8. public
  9.     int Func(int x, int y); 
  10. }; 
  11.   
  12. A a; 
  13. auto bf2 = std::bind(&A::Func, a, std::placeholders::_1, std::placeholders::_2); 
  14. bf2(10, 20); ///< same as a.Func(10, 20) 
  15.   
  16. std::function< int(int)> bf3 = std::bind(&A::Func, a, std::placeholders::_1, 100); 
  17. bf3(10); ///< same as a.Func(10, 100) 
#include < functional>
 
int Func(int x, int y);
auto bf1 = std::bind(Func, 10, std::placeholders::_1);
bf1(20); ///< same as Func(10, 20)
 
class A
{
public:
    int Func(int x, int y);
};
 
A a;
auto bf2 = std::bind(&A::Func, a, std::placeholders::_1, std::placeholders::_2);
bf2(10, 20); ///< same as a.Func(10, 20)
 
std::function< int(int)> bf3 = std::bind(&A::Func, a, std::placeholders::_1, 100);
bf3(10); ///< same as a.Func(10, 100)

六、lambda     一般说到匿名函数或者闭包(closure)就是跟这个东西有关了,这个比上面两个东西更加“高级”了些,但是同样的概念在其他语言中是非常常见的基本语法。

 

无论是python,lua,还是java,objective-c匿名函数都非常常用。   示例如下:

  1. vector< int> vec; 
  2. /// 1. simple lambda 
  3. auto it = std::find_if(vec.begin(), vec.end(), [](int i) { return i > 50; }); 
  4. class
  5. public
  6.     bool operator(int i) const { return i > 50; } 
  7. }; 
  8. auto it = std::find_if(vec.begin(), vec.end(), A()); 
  9.   
  10. /// 2. lambda return syntax 
  11. std::function< int(int)> square = [](int i) -> int { return i * i; } 
  12.   
  13. /// 3. lambda expr: capture of local variable 
  14.     int min_val = 10; 
  15.     int max_val = 1000; 
  16.   
  17.     auto it = std::find_if(vec.begin(), vec.end(), [=](int i) { 
  18.         return i > min_val && i < max_val;  
  19.         }); 
  20.   
  21.     auto it = std::find_if(vec.begin(), vec.end(), [&](int i) { 
  22.         return i > min_val && i < max_val; 
  23.         }); 
  24.   
  25.     auto it = std::find_if(vec.begin(), vec.end(), [=, &max_value](int i) { 
  26.         return i > min_val && i < max_val; 
  27.         }); 
  28.   
  29. /// 4. lambda expr: capture of class member 
  30. class
  31. public
  32.     void DoSomething(); 
  33.   
  34. private
  35.     std::vector<int>  m_vec; 
  36.     int               m_min_val; 
  37.     int               m_max_va; 
  38. }; 
  39.   
  40. /// 4.1 capture member by this 
  41. void A::DoSomething() 
  42.     auto it = std::find_if(m_vec.begin(), m_vec.end(), [this](int i){ 
  43.         return i > m_min_val && i < m_max_val; }); 
  44.   
  45. /// 4.2 capture member by default pass-by-value 
  46. void A::DoSomething() 
  47.     auto it = std::find_if(m_vec.begin(), m_vec.end(), [=](int i){ 
  48.         return i > m_min_val && i < m_max_val; }); 
  49.   
  50. /// 4.3 capture member by default pass-by-reference 
  51. void A::DoSomething() 
  52.     auto it = std::find_if(m_vec.begin(), m_vec.end(), [&](int i){ 
  53.         return i > m_min_val && i < m_max_val; }); 
vector< int> vec;
/// 1. simple lambda
auto it = std::find_if(vec.begin(), vec.end(), [](int i) { return i > 50; });
class A
{
public:
    bool operator(int i) const { return i > 50; }
};
auto it = std::find_if(vec.begin(), vec.end(), A());
 
/// 2. lambda return syntax
std::function< int(int)> square = [](int i) -> int { return i * i; }
 
/// 3. lambda expr: capture of local variable
{
    int min_val = 10;
    int max_val = 1000;
 
    auto it = std::find_if(vec.begin(), vec.end(), [=](int i) {
        return i > min_val && i < max_val; 
        });
 
    auto it = std::find_if(vec.begin(), vec.end(), [&](int i) {
        return i > min_val && i < max_val;
        });
 
    auto it = std::find_if(vec.begin(), vec.end(), [=, &max_value](int i) {
        return i > min_val && i < max_val;
        });
}
 
/// 4. lambda expr: capture of class member
class A
{
public:
    void DoSomething();
 
private:
    std::vector<int>  m_vec;
    int               m_min_val;
    int               m_max_va;
};
 
/// 4.1 capture member by this
void A::DoSomething()
{
    auto it = std::find_if(m_vec.begin(), m_vec.end(), [this](int i){
        return i > m_min_val && i < m_max_val; });
}
 
/// 4.2 capture member by default pass-by-value
void A::DoSomething()
{
    auto it = std::find_if(m_vec.begin(), m_vec.end(), [=](int i){
        return i > m_min_val && i < m_max_val; });
}
 
/// 4.3 capture member by default pass-by-reference
void A::DoSomething()
{
    auto it = std::find_if(m_vec.begin(), m_vec.end(), [&](int i){
        return i > m_min_val && i < m_max_val; });
}

把上面三点结合起来使用,C++将会变得非常强大,有点函数式编程的味道了。对于用bind来生成function和用lambda表达式来生成function, 通常情况下两种都是ok的,但是在参数多的时候,bind要传入很多的std::placeholders,而且看着没有lambda表达式直观,所以通常建议优先考虑使用lambda表达式。

 

 

七、thread mutex和condition_variable   多线程相关内容终于标准化了,多线程的代码也不会再有一大堆#ifdef 来区分不同平台了。 使用起来也很方便(就是boost::thread)

1、thread

  1. #include < iostream> 
  2. #include < string> 
  3. #include < thread> 
  4.   
  5. class Printer 
  6. public
  7.     void Print(int id, std::string& name) 
  8.     {    
  9.         std::cout < < "id=" << id << ", name=" << name; 
  10.     }    
  11. }; 
  12.   
  13. void Hello() 
  14.     std::cout << "hello world" << std::endl; 
  15.   
  16. int main() 
  17.     Printer p; 
  18.     int id = 1; 
  19.     std::string name("xiao5ge"); 
  20.   
  21.     std::thread t1(&Printer::Print, p, id, name); 
  22.     std::thread t2(std::bind(&Printer::Print, p, id, name)); 
  23.     std::thread t3([&]{ p.Print(id, name); });  
  24.     std::thread t4(Hello); 
  25.   
  26.     t4.join(); 
  27.     t3.join(); 
  28.     t2.join(); 
  29.     t1.join(); 
#include < iostream>
#include < string>
#include < thread>
 
class Printer
{
public:
    void Print(int id, std::string& name)
    {   
        std::cout < < "id=" << id << ", name=" << name;
    }   
};
 
void Hello()
{
    std::cout << "hello world" << std::endl;
}
 
int main()
{
    Printer p;
    int id = 1;
    std::string name("xiao5ge");
 
    std::thread t1(&Printer::Print, p, id, name);
    std::thread t2(std::bind(&Printer::Print, p, id, name));
    std::thread t3([&]{ p.Print(id, name); }); 
    std::thread t4(Hello);
 
    t4.join();
    t3.join();
    t2.join();
    t1.join();
}

2、mutex

 

  1. #include < mutex> 
  2.   
  3. // global vars 
  4. int data = 0; 
  5. std::mutex data_mutex; 
  6.   
  7. // thread 1 
  8.     std::lock_guard< std::mutex> locker(data_mutex); 
  9.     data = 1; 
  10.   
  11. // thread 2 
  12.     std::lock_guard< std::mutex> locker(data_mutex); 
  13.     data = 2; 
#include < mutex>
 
// global vars
int data = 0;
std::mutex data_mutex;
 
// thread 1
{
    std::lock_guard< std::mutex> locker(data_mutex);
    data = 1;
}
 
// thread 2
{
    std::lock_guard< std::mutex> locker(data_mutex);
    data = 2;
}

3、condition_variable  条件变量其实就是实现这么一个效果,如果变量不为真,则线程挂起,如果为真,则线程唤醒。

 

  1. // global 
  2. std::atomic< bool> is_finish(false); 
  3. std::mutex finish_mutex; 
  4. std::condition_variable finish_cond; 
  5.   
  6. // thread 1 
  7.     std::unique< std::mutex> locker(finish_mutex); 
  8.   
  9.     // 1. loop wait 
  10.     while (!is_finish) 
  11.     {    
  12.         finish_cond.wait(locker); 
  13.     }    
  14.   
  15.     // 2. wait until prediction is true, loop inside 
  16.     finish_cond.wait(locker, []{ return is_finish; });  
  17.   
  18.     // 3. wait until eithor prediction is true or timeout 
  19.     finish_cond.wait(locker, std::chrono::seconds(1), 
  20.             []{ return is_finish; });  
  21.   
  22. // thread 2 
  23.     is_finish = true
  24.   
  25.     // 1. notify one of the waiter 
  26.     finish_cond.notify_one(); 
  27.   
  28.     // 2. notify all the waiter 
  29.     finish_cond.notify_all(); 
// global
std::atomic< bool> is_finish(false);
std::mutex finish_mutex;
std::condition_variable finish_cond;
 
// thread 1
{
    std::unique< std::mutex> locker(finish_mutex);
 
    // 1. loop wait
    while (!is_finish)
    {   
        finish_cond.wait(locker);
    }   
 
    // 2. wait until prediction is true, loop inside
    finish_cond.wait(locker, []{ return is_finish; }); 
 
    // 3. wait until eithor prediction is true or timeout
    finish_cond.wait(locker, std::chrono::seconds(1),
            []{ return is_finish; }); 
}
 
// thread 2
{
    is_finish = true;
 
    // 1. notify one of the waiter
    finish_cond.notify_one();
 
    // 2. notify all the waiter
    finish_cond.notify_all();
}

关于条件变量的补充:

 

a、condition_variable: 用在std::unique_lock< std::mutex>上wait, 比较高效。condition_variable_any: 可以用在任意mutex上wait, 比较灵活,但效率比condition_variable差一些。

b、关于wait,有三种基本的用法:第1种是在指定的条件上循环等待,直到条件为真notify时才会继续执行后面的逻辑;第2种用法语义上和第1种是一样的,但是不是用户做显式的loop等待,用户传入一个需要满足的条件的closure, wait一直等到这个条件为真被notify时才会返回继续执行下面的逻辑,可以理解为这时候,在wait内部有一个loop;第3 种用法,加了一个超时的语义,wait一直等到条件为真或者超时,被notify时才会返回继续执行下面的逻辑。

c、关于notify, 有两种:第1种是notify_one, 只唤醒一个在wait的线程; 第2种是notify_all,唤醒所有在wait的线程,相当于一个broadcast的语义

 

八、一些小的修改:

1、“>>”可以做为嵌套模板的闭合端,不需要显式的加空格

  1. std::vector< std::list<int>> vi1;  /// fine in C++0x, error in C++98 
  2. std::vector< std::list<int> > vi2; /// fine in C++0x and C++98 
std::vector< std::list<int>> vi1;  /// fine in C++0x, error in C++98
std::vector< std::list<int> > vi2; /// fine in C++0x and C++98

2、新增加空指针类型nullptr。 这个主要是因为NULL的宏定义无法区分是指针还是整型,那么重载或者模板推倒时就会出现错误。其他情况下nullptr与NULL相同。

 

3、 unicode的支持,新增加char16_t和char32_t类型

  1. 'x'   /// char 'x' 
  2. L'x'  /// wchar_t 'x' 
  3. u'x'  /// char16_t 'x', UCS-2 
  4. U'X'  /// char32_t 'x' UCS-4 
  5.   
  6. /// std::basic_string typedefs for all character types: 
  7. std::string s1;    /// std::basic_string< char> 
  8. std::wstring s2;   /// std::basic_string< wchar_t> 
  9. std::u16string s3; /// std::basic_string< char16_t> 
  10. std::u32string s4; /// std::basic_string< char32_t> 
'x'   /// char 'x'
L'x'  /// wchar_t 'x'
u'x'  /// char16_t 'x', UCS-2
U'X'  /// char32_t 'x' UCS-4
 
/// std::basic_string typedefs for all character types:
std::string s1;    /// std::basic_string< char>
std::wstring s2;   /// std::basic_string< wchar_t>
std::u16string s3; /// std::basic_string< char16_t>
std::u32string s4; /// std::basic_string< char32_t>

4、原字符串的支持(raw string),等同于python中的r“xxxx”,在这个字符串中\r\n这样的转义字符不再被转义。

 

  1. // c++98 
  2. std::string noNewlines("(\\n\\n)"); 
  3. std::string cmd("(ls /home/docs | grep \".pdf\")"); 
  4.  
  5. // c++0x 
  6. std::string noNewlines(R"(\n\n)"); 
  7. std::string cmd(R"(ls /home/docs | grep ".pdf")"); 
// c++98
std::string noNewlines("(\\n\\n)");
std::string cmd("(ls /home/docs | grep \".pdf\")");

// c++0x
std::string noNewlines(R"(\n\n)");
std::string cmd(R"(ls /home/docs | grep ".pdf")");

raw string的分隔符可以灵活指定,用户可以指定各种各样的分隔符,原则就是不跟raw string text中的字符不相冲突即可,例如下面的例子

 

  1. std::regex re1(R"!("operator\(\)"|"operator->")!"); /// "operator()"| "operator->" 
  2. std::regex re2(R"xyzzy("\([A-Za-z_]\w*\)")xyzzy");  /// "(identifier)" 
std::regex re1(R"!("operator\(\)"|"operator->")!"); /// "operator()"| "operator->"
std::regex re2(R"xyzzy("\([A-Za-z_]\w*\)")xyzzy");  /// "(identifier)"

 

5、变参模板,模板参数可以像printf一样变参了,不过我模板用的较少,感觉会用到这个的情况也不多,就不多介绍了。

你可能感兴趣的:(C++)