好吧,之前我错误的认为c++0x就是添加了一些大多数人不懂的语法,比如bind,lambda,或者是一些使用起来可有可无的特性,比如auto,或者是可以通过具体代码方式来避免的,比如move。
不过阅读了漫话c++0x系列文章,我发现c++0x真的是一门新的语言,并且是足够漂亮的新语言。说实话,我们平时写代码确实不会遇到什么复杂的语法(如果自己写了自以为很炫,但是别人都看不懂的语句,就自行面壁去)。c++一些容易产生bug或者是非常难以理解的东西应该是我们尽量去避免的(比如,多继承和模板),模板不是不可以用,而是应该限定到非常有限的地方。
但是随着c++0x的普及(各主流编译器都已经支持),学习使用c++新语法就变得非常有必要了。通过下面介绍的一些特性,我们的代码可以变得更加简洁,从而可以使我们的代码更加容易阅读(无论是对他人而言,还是对自己而言)。
=============================分割线=================================
一、auto用来声明变量
把这个列在第一位是因为这个是使用最广泛并且最容易被使用的。它最常见的地方就是容器遍历,请看下面的代码:
- // 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
- }
// c++98std::map< std::string, std::vector > mp;for (std::map< std::string, std::vector >::iterator i = mp.begin(); i != mp.end(); ++i){ /// traverse the map}// c++0xstd::map< std::string, std::vector > mp;for (auto i = mp.begin(); i != mp.end(); ++i){ /// traverse the map}
二、for循环新的遍历方式(像java一样的遍历方式)
- // 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++98std::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++0xstd::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的的统一初始化逻辑中,参数的个数可以少于成员,但不能多于,如果少于的话,没初始化的成员通过缺省的方式初始化
- /// c++98中,各种形式的初始化
- constint x(5); ///< 直接初始化
- constint 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 = 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]; ///< 不能在这里初始化堆上内存
- /// 与上面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 = 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};
除了上面介绍这些,还有一些更酷的写法
- void Func(const std::vector< int>& v);
- Func({1, 2, 3, 4, 5});
- 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,简单示例如下
- #include < functional>
- std::function< size_t(constchar*)> print_func;
- /// normal function -> std::function object
- size_t CPrint(constchar*) { ... }
- print_func = CPrint;
- print_func("hello world"):
- /// functor -> std::function object
- class CxxPrint
- {
- public:
- size_t operator()(constchar*) { ... }
- };
- CxxPrint p;
- print_func = p;
- print_func("hello world");
#include < functional> std::function< size_t(const char*)> print_func; /// normal function -> std::function objectsize_t CPrint(const char*) { ... }print_func = CPrint;print_func("hello world"): /// functor -> std::function objectclass CxxPrint{public: size_t operator()(const char*) { ... }};CxxPrint p;print_func = p;print_func("hello world");
五、bind 其实就是boost::bind,一般和function配合起来使用,用来替代原来stl中丑陋的bind1st和bind2nd,示例如下
- #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)
#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匿名函数都非常常用。 示例如下:
- 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; });
- }
vector< int> vec;/// 1. simple lambdaauto 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 syntaxstd::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 memberclass A{public: void DoSomething(); private: std::vector m_vec; int m_min_val; int m_max_va;}; /// 4.1 capture member by thisvoid 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-valuevoid 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-referencevoid 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
- #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();
- }
#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
- #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;
- }
#include < mutex> // global varsint 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 条件变量其实就是实现这么一个效果,如果变量不为真,则线程挂起,如果为真,则线程唤醒。
- // 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();
- }
// globalstd::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、“>>”可以做为嵌套模板的闭合端,不需要显式的加空格
- 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
std::vector< std::list > vi1; /// fine in C++0x, error in C++98std::vector< std::list > vi2; /// fine in C++0x and C++98
2、新增加空指针类型nullptr。 这个主要是因为NULL的宏定义无法区分是指针还是整型,那么重载或者模板推倒时就会出现错误。其他情况下nullptr与NULL相同。
3、 unicode的支持,新增加char16_t和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>
'x' /// char 'x'L'x' /// wchar_t 'x'u'x' /// char16_t 'x', UCS-2U'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这样的转义字符不再被转义。
- // 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")");
// c++98std::string noNewlines("(\\n\\n)");std::string cmd("(ls /home/docs | grep \".pdf\")");// c++0xstd::string noNewlines(R"(\n\n)");std::string cmd(R"(ls /home/docs | grep ".pdf")");
raw string的分隔符可以灵活指定,用户可以指定各种各样的分隔符,原则就是不跟raw string text中的字符不相冲突即可,例如下面的例子
- std::regex re1(R"!("operator\(\)"|"operator->")!"); /// "operator()"| "operator->"
- 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一样变参了,不过我模板用的较少,感觉会用到这个的情况也不多,就不多介绍了。