C++11新特性之 std::future and std::async

最后更新于:2022-04-01 06:32:33

先来个小插曲,百度翻译,你够了:  ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-12-29_56824e4a4f1c0.jpg) **std::future** 设想这样的情况,你希望一个线程做一些事情,然后返回你一个结果。同时,你在做一些其他的工作,该工作也许会也许不会花费你一点时间。你希望在某个特定的时间获取那个线程的结果。  在win32中,你可以这样  用CreateThread启动线程  在线程里,启动任务,当准备完毕后发送一个事件(event),并把结果放在全局变量里。  在主函数里(main)做其它的事情,然后在你想要结果的地方,调用WaitForSingleObject  在c++11,这个可以轻松被std::future实现,然后返回任何类型,因为它是一个模板。 std::future 究竟是什么呢?简单地说,std::future 可以用来获取异步任务的结果,因此可以把它当成一种简单的线程间同步的手段。 让我们考虑一个简单的事儿:用线程计算一些值: ~~~ std::thread t([]() { auto res = perform_long_computation(); }); ~~~ std::thread在之前的博客中已经介绍过了。我们还可以更高效吗? ~~~ MyResult sharedRes; std::thread t([&]() { sharedRes = perform_long_computation(); }); ~~~ 这个时候,我们应该知道thread什么时候执行结束,这里有一个很简单的方式 ~~~ auto result = std::async([]() { return perform_long_computation(); }); MyResult finalResult = result.get(); ~~~ 上诉代码很简洁明了吧,我们需要弄清其原理。  英文描述可能更加确切:  std::future holds a shared state  std::async allow us to run the code asynchronously.  因此,有了这样的代码: ~~~ std::future<MyResult> result = std::async([]() { return perform_long_computation(); }); MyResult finalResult = result.get(); ~~~ 上一段完整的代码: ~~~ // future example #include <iostream> // std::cout #include <future> // std::async, std::future #include <chrono> // std::chrono::milliseconds // a non-optimized way of checking for prime numbers: bool is_prime (int x) { for (int i=2; i<x; ++i) if (x%i==0) return false; return true; } int main () { // call function asynchronously: std::future<bool> fut = std::async (is_prime,444444443); // do something while waiting for function to set future: std::cout << "checking, please wait"; std::chrono::milliseconds span (100); while (fut.wait_for(span)==std::future_status::timeout) std::cout << '.' << std::flush; bool x = fut.get(); // retrieve return value std::cout << "\n444444443 " << (x?"is":"is not") << " prime.\n"; return 0; } ~~~ 输出:  checking, please wait……………………  444444443 is prime. **std::async**  为什么要用std::async代替线程的创建 std::async是为了让用户的少费点脑子的,它让这三个对象默契的工作。大概的工作过程是这样的:std::async先将异步操作用std::packaged_task包装起来,然后将异步操作的结果放到std::promise中,这个过程就是创造未来的过程。外面再通过future.get/wait来获取这个未来的结果,怎么样,std::async真的是来帮忙的吧,你不用再想到底该怎么用std::future、std::promise和std::packaged_task了,std::async已经帮你搞定一切了! 现在来看看std::async的原型 ~~~ async(std::launch::async | std::launch::deferred, f, args...) ~~~ 第一个参数是线程的创建策略,有两种策略,默认的策略是立即创建线程:  std::launch::async:在调用async就开始创建线程。  std::launch::deferred:延迟加载方式创建线程。调用async时不创建线程,直到调用了future的get或者wait时才创建线程。  第二个参数是线程函数,第三个参数是线程函数的参数。 ~~~ std::future<int> future = std::async(std::launch::async, [](){ std::this_thread::sleep_for(std::chrono::seconds(3)); return 8; }); std::cout << "waiting...\n"; std::future_status status; do { status = future.wait_for(std::chrono::seconds(1)); if (status == std::future_status::deferred) { std::cout << "deferred\n"; } else if (status == std::future_status::timeout) { std::cout << "timeout\n"; } else if (status == std::future_status::ready) { std::cout << "ready!\n"; } } while (status != std::future_status::ready); std::cout << "result is " << future.get() << '\n'; ~~~ 可能的结果:  waiting…  timeout  timeout  ready!  result is 8 这里写代码片  “`
';

开始使用C++11的几个理由

最后更新于:2022-04-01 06:32:31

C++11新特性介绍了一段时间,至今为止也差不多了。是要总结的时候了:  你可能会问我,为什么需要C++11呢,我这就给出理由: **理由1**:《[C++11新特性之 Move semantics(移动语义)](http://blog.csdn.net/wangshubo1989/article/details/49748703 "move")》  move语义(move semantics)。简单的说,它是优化复制的一种方式。有时候复制很显然是浪费的。如果你从一个临时的string对象复制内容,简单的复制指针到字符缓冲区将比创建一个新的缓冲区再复制要高效得多。他之所以能工作是因为源对象超出了范围。  然而,在这以前C++并没有判断源对象是不是临时对象的机制。move语义通过除了复制操作外还允许你有一个move构造函数(move constructor)和一个move赋值运算(move assignment)符来提供这个机制。  当你在Visual Studio 2010中使用标准库中的类如string或vector时,它们已经支持move语义了。这可以防止不必要的的复制从而改善性能。  通过在你的类中实现move语义你可以获得额外的性能提升,比如当你把它们存储到STL容器中时。还有,move语义不仅可以应用到构造函数,还可以应用到方法(如vector的push_back方法)。 **理由2:**《[吐血整理C++11新特性](http://blog.csdn.net/wangshubo1989/article/details/48490035 "auto")》  auto关键字可以自动推断类型,所以下面的代码: ~~~ vector<vector<MyType>>::const_iterator it = v.begin() ~~~ 现在可以很简单的写成: ~~~ auto it = v.cbegin() ~~~ 尽管有些人会说,它隐藏了类型信息,在我看来它利大于弊,因为它减少了视觉混换并展示了代码的行为,还有它可以让你我少打很多字!  理由5:Lambda表达式提供了一种方法来定义匿名方法对象(实际上是闭包),这是代码更加线性和有规律可循。这在和STL算法结合使用时很方便:  bool is_fuel_level_safe()  {  return all_of(_tanks.begin(), _tanks.end(),  [this](Tank& t) { return t.fuel_level() > _min_fuel_level; });  } **理由3:**新的智能指针(smart pointer)替换了有问题的auto_ptr,你可以不用担心内存的释放并移除相关释放内存的代码了。这让代码更清晰,并杜绝了内存泄露和查找内存泄露的时间。 **理由4**:《[C++11新特性之std::function](http://blog.csdn.net/wangshubo1989/article/details/49134235 "function")》把方法作为first class object是一个非常强大的特性,这让你的代码变得更灵活和通用了。C++的std::function提供了这方面的功能。方法提供一种包装和传递任何可调用的东西-函数指针, 仿函数(functor), lambda表达式等。 **理由5**:《[c++11特性之override和final关键字](http://blog.csdn.net/wangshubo1989/article/details/49539251 "final override")》还有许多其它小的功能,如override、final关键字和nullptr让你的代码意图更明确。对我来说,减少视觉混乱和代码中能够更清楚地表达我的意图意味着更高兴、更高效。 **理由6**:《[C++11新特性之 Static assertions 和constructor delegation](http://blog.csdn.net/wangshubo1989/article/details/49786813 "static assertion")》  C++ 11提供了一种方法来检查先决条件并尽早的在可能的时机捕获错误-编译过程  这是通过静态断言(static_assert)和类别属性模版实现的。这种方法的另一个好处是,它不需要占用任何的运行时开销,没有什么性能损失!
';

C++11新特性之 Static assertions 和constructor delegation

最后更新于:2022-04-01 06:32:29

C++11新特性继续。  **Static assertion**  static_assert 是在编译时期的断言,作用不言而喻的。  语法是这样: ~~~ static_assert ( bool_constexpr , string ) ~~~ 其中:  bool_constexpr: 常量表达式  string: 如果bool_constexpr表达式为false, 这个string就是编译时候报的错误。 看看代码: ~~~ // run-time assert assert(ptr != NULL) // C++ 11 // compile-time assert static_assert(sizeof(void *) == 4, "64-bit is not supported."); ~~~ **Constructor delegation**  之前我们知道,一个类的构造函数不可以调用这个类的其他构造函数。每个构造函数只能包含类的成员变量和共有函数。 ~~~ // C++03 class A { void init() { std::cout << "init()"; } void doSomethingElse() { std::cout << "doSomethingElse()\n"; } public: A() { init(); } A(int a) { init(); doSomethingElse(); } }; ~~~ 但是C++11允许我们这么干! ~~~ // C++11 class A { void doSomethingElse() { std::cout << "doSomethingElse()\n"; } public: A() { ... } A(int a) : A() { doSomethingElse(); } }; ~~~ 然后,此时此刻应该有个警告:  wiki :  C++03 considers an object to be constructed when its constructor finishes executing, but C++11 considers an object constructed once any constructor finishes execution. Since multiple constructors will be allowed to execute, this will mean that each delegating constructor will be executing on a fully constructed object of its own type. Derived class constructors will execute after all delegation in their base classes is complete. For base-class constructors, C++11 allows a class to specify that base class constructors will be inherited. This means that the C++11 compiler will generate code to perform the inheritance, the forwarding of the derived class to the base class. Note that this is an all-or-nothing feature; either all of that base class’s constructors are forwarded or none of them are. Also, note that there are restrictions for multiple inheritance, such that class constructors cannot be inherited from two classes that use constructors with the same signature. Nor can a constructor in the derived class exist that matches a signature in the inherited base class. Note that in C++03, non-constant data members of classes cannot be initialized at the site of the declaration of those members. They can be initialized only in a constructor. In C++11, now it’s allowed: ~~~ // C++11 class A { int a = 99 void doSomethingElse() { std::cout << "doSomethingElse()\n"; } public: A() { ... } A(int a) : A() { doSomethingElse(); } }; ~~~ **拷贝构造函数使用constructor delegation** ~~~ A(const A& b) : A() { m_a = b.m_a; } ~~~
';

C++11新特性之 default and delete specifiers

最后更新于:2022-04-01 06:32:26

C++11的东西介绍的差不多了,今天介绍两个关键字default和delete! **default**  首先我们清楚,如果自己提供了任何形式的构造函数,那么编译器将不会产生一个默认构造函数,这是一个放之四海而皆准的原则。 但凡是都是双面性,看看一下的代码: ~~~ class A { public: A(int a){}; }; ~~~ 然后我们这样使用: ~~~ A a; ~~~ 悲剧发生了,编译器不会为我们提供默认的构造函数! 所以呢,我们要暴力一些,对编译器做一些强制的规定。这时候default关键字出场了: ~~~ class A { public: A(int a){} A() = default; }; ~~~ 于是再也不用担心下面的代码报错了: ~~~ A a; ~~~ 下面谈一谈delete  **delete**  有这样一个类: ~~~ class A { public: A(int a){}; }; ~~~ 下面的使用都会正确: ~~~ A a(10); // OK A b(3.14); // OK 3.14 will be converted to 3 a = b; // OK We have a compiler generated assignment operator ~~~ 然而,如果我们不想让上面的某种调用成功呢,比如不允许double类型作为参数呢? 你当然想到了,关键字delete: ~~~ class A { public: A(int a){}; A(double) = delete; // conversion disabled A& operator=(const A&) = delete; // assignment operator disabled }; ~~~ 这时候你使用代码: ~~~ A a(10); // OK A b(3.14); // Error: conversion from double to int disabled a = b; // Error: assignment operator disabled ~~~ 通过关键字default和delete 我们随心所欲!!!!
';

C++11新特性之 Move semantics(移动语义)

最后更新于:2022-04-01 06:32:24

按值传递的意义是什么?  当一个函数的参数按值传递时,这就会进行拷贝。当然,编译器懂得如何去拷贝。  而对于我们自定义的类型,我们也许需要提供拷贝构造函数。 但是不得不说,拷贝的代价是昂贵的。 所以我们需要寻找一个避免不必要拷贝的方法,即C++11提供的移动语义。  上一篇博客中有一个句话用到了: ~~~ #include <iostream> void f(int& i) { std::cout << "lvalue ref: " << i << "\n"; } void f(int&& i) { std::cout << "rvalue ref: " << i << "\n"; } int main() { int i = 77; f(i); // lvalue ref called f(99); // rvalue ref called f(std::move(i)); // 稍后介绍 return 0; } ~~~ 实际上,右值引用主要用于创建移动构造函数和移动赋值运算。 移动构造函数类似于拷贝构造函数,把类的实例对象作为参数,并创建一个新的实例对象。  但是 移动构造函数可以避免**内存的重新分配**,因为我们知道右值引用提供了一个暂时的对象,而不是进行copy,所以我们可以进行移动。 换言之,在设计到关于临时对象时,右值引用和移动语义允许我们避免不必要的拷贝。我们不想拷贝将要消失的临时对象,所以这个临时对象的资源可以被我们用作于其他的对象。 右值就是典型的临时变量,并且他们可以被修改。如果我们知道一个函数的参数是一个右值,我们可以把它当做一个临时存储。这就意味着我们要移动而不是拷贝右值参数的内容。这就会节省很多的空间。 说多无语,看代码: ~~~ #include <iostream> #include <algorithm> class A { public: // Simple constructor that initializes the resource. explicit A(size_t length) : mLength(length), mData(new int[length]) { std::cout << "A(size_t). length = " << mLength << "." << std::endl; } // Destructor. ~A() { std::cout << "~A(). length = " << mLength << "."; if (mData != NULL) { std::cout << " Deleting resource."; delete[] mData; // Delete the resource. } std::cout << std::endl; } // Copy constructor. A(const A& other) : mLength(other.mLength), mData(new int[other.mLength]) { std::cout << "A(const A&). length = " << other.mLength << ". Copying resource." << std::endl; std::copy(other.mData, other.mData + mLength, mData); } // Copy assignment operator. A& operator=(const A& other) { std::cout << "operator=(const A&). length = " << other.mLength << ". Copying resource." << std::endl; if (this != &other) { delete[] mData; // Free the existing resource. mLength = other.mLength; mData = new int[mLength]; std::copy(other.mData, other.mData + mLength, mData); } return *this; } // Move constructor. A(A&& other) : mData(NULL), mLength(0) { std::cout << "A(A&&). length = " << other.mLength << ". Moving resource.\n"; // Copy the data pointer and its length from the // source object. mData = other.mData; mLength = other.mLength; // Release the data pointer from the source object so that // the destructor does not free the memory multiple times. other.mData = NULL; other.mLength = 0; } // Move assignment operator. A& operator=(A&& other) { std::cout << "operator=(A&&). length = " << other.mLength << "." << std::endl; if (this != &other) { // Free the existing resource. delete[] mData; // Copy the data pointer and its length from the // source object. mData = other.mData; mLength = other.mLength; // Release the data pointer from the source object so that // the destructor does not free the memory multiple times. other.mData = NULL; other.mLength = 0; } return *this; } // Retrieves the length of the data resource. size_t Length() const { return mLength; } private: size_t mLength; // The length of the resource. int* mData; // The resource. }; ~~~ **移动构造函数**  语法: ~~~ A(A&& other) noexcept // C++11 - specifying non-exception throwing functions { mData = other.mData; // shallow copy or referential copy other.mData = nullptr; } ~~~ 最主要的是没有用到新的资源,是移动而不是拷贝。  假设一个地址指向了一个有一百万个int元素的数组,使用move构造函数,我们没有创造什么,所以代价很低。 ~~~ // Move constructor. A(A&& other) : mData(NULL), mLength(0) { // Copy the data pointer and its length from the // source object. mData = other.mData; mLength = other.mLength; // Release the data pointer from the source object so that // the destructor does not free the memory multiple times. other.mData = NULL; other.mLength = 0; } ~~~ **移动比拷贝更快!!!** **移动赋值运算符**  语法: ~~~ A& operator=(A&& other) noexcept { mData = other.mData; other.mData = nullptr; return *this; } ~~~ 工作流程这样的:Google上这么说的: Release any resources that *this currently owns.  Pilfer other’s resource.  Set other to a default state.  Return *this. ~~~ // Move assignment operator. A& operator=(A&& other) { std::cout << "operator=(A&&). length = " << other.mLength << "." << std::endl; if (this != &other) { // Free the existing resource. delete[] mData; // Copy the data pointer and its length from the // source object. mData = other.mData; mLength = other.mLength; // Release the data pointer from the source object so that // the destructor does not free the memory multiple times. other.mData = NULL; other.mLength = 0; } return *this; } ~~~ 让我们看几个move带来的好处吧!  vector众所周知,C++11后对vector也进行了一些优化。例如vector::push_back()被定义为了两种版本的重载,一个是cosnt T&左值作为参数,一个是T&&右值作为参数。例如下面的代码: ~~~ std::vector<A> v; v.push_back(A(25)); v.push_back(A(75)); ~~~ 上面两个push_back()都会调用push_back(T&&)版本,因为他们的参数为右值。这样提高了效率。 而 当参数为左值的时候,会调用push_back(const T&) 。 ~~~ #include <vector> int main() { std::vector<A> v; A aObj(25); // lvalue v.push_back(aObj); // push_back(const T&) } ~~~ 但事实我们可以使用 static_cast进行强制: ~~~ // calls push_back(T&&) v.push_back(static_cast<A&&>(aObj)); ~~~ 我们可以使用std::move完成上面的任务: ~~~ v.push_back(std::move(aObj)); //calls push_back(T&&) ~~~ 似乎push_back(T&&)永远是最佳选择,但是一定要记住:  push_back(T&&) 使得参数为空。如果我们想要保留参数的值,我们这个时候需要使用拷贝,而不是移动。 最后写一个例子,看看如何使用move来交换两个对象: ~~~ #include <iostream> using namespace std; class A { public: // constructor explicit A(size_t length) : mLength(length), mData(new int[length]) {} // move constructor A(A&& other) { mData = other.mData; mLength = other.mLength; other.mData = nullptr; other.mLength = 0; } // move assignment A& operator=(A&& other) noexcept { mData = other.mData; mLength = other.mLength; other.mData = nullptr; other.mLength = 0; return *this; } size_t getLength() { return mLength; } void swap(A& other) { A temp = move(other); other = move(*this); *this = move(temp); } int* get_mData() { return mData; } private: int *mData; size_t mLength; }; int main() { A a(11), b(22); cout << a.getLength() << ' ' << b.getLength() << endl; cout << a.get_mData() << ' ' << b.get_mData() << endl; swap(a,b); cout << a.getLength() << ' ' << b.getLength() << endl; cout << a.get_mData() << ' ' << b.get_mData() << endl; return 0; } ~~~
';

C++11新特性之 rvalue Reference(右值引用)

最后更新于:2022-04-01 06:32:22

右值引用可以使我们区分表达式的左值和右值。 C++11引入了右值引用的概念,使得我们把引用与右值进行绑定。使用两个“取地址符号”: ~~~ int&& rvalue_ref = 99; ~~~ 需要注意的是,只有左值可以付给引用,如: ~~~ int& ref = 9; ~~~ 我们会得到这样的错误: “invalid initialization of non-const reference of type int& from an rvalue of type int” 我们只能这样做: ~~~ int nine = 9; int& ref = nine; ~~~ 看下面的例子,你会明白的: ~~~ #include <iostream> void f(int& i) { std::cout << "lvalue ref: " << i << "\n"; } void f(int&& i) { std::cout << "rvalue ref: " << i << "\n"; } int main() { int i = 77; f(i); // lvalue ref called f(99); // rvalue ref called f(std::move(i)); // 稍后介绍 return 0; } ~~~ 右值有更隐晦的,记住如果一个表达式的结果是一个暂时的对象,那么这个表达式就是右值,同样看看代码: ~~~ #include <iostream> int getValue () { int ii = 10; return ii; } int main() { std::cout << getValue(); return 0; } ~~~ 这里需要注意的是 getValue() 是一个右值。 我们只能这样做,必须要有const ~~~ const int& val = getValue(); // OK int& val = getValue(); // NOT OK ~~~ 但是C++11中的右值引用允许这样做: ~~~ const int&& val = getValue(); // OK int&& val = getValue(); // OK ~~~ 现在做一个比较吧: ~~~ void printReference (const int& value) { cout << value; } void printReference (int&& value) { cout << value; } ~~~ 第一个printReference()可以接受参数为左值,也可以接受右值。  而第二个printReference()只能接受右值引用作为参数。 In other words, by using the rvalue references, we can use function overloading to determine whether function parameters are lvalues or rvalues by having one overloaded function taking an lvalue reference and another taking an rvalue reference. In other words, C++11 introduces a new non-const reference type called an rvalue reference, identified by T&&. This refers to temporaries that are permitted to be modified after they are initialized, which is the cornerstone of move semantics. ~~~ #include <iostream> using namespace std; void printReference (int& value) { cout << "lvalue: value = " << value << endl; } void printReference (int&& value) { cout << "rvalue: value = " << value << endl; } int getValue () { int temp_ii = 99; return temp_ii; } int main() { int ii = 11; printReference(ii); printReference(getValue()); // printReference(99); return 0; } /*---------------------- 输出 lvalue: value = 11 rvalue: value = 99 ----------------------*/ ~~~
';

C++11新特性之 nullptr

最后更新于:2022-04-01 06:32:19

我们知道在编程的世界里,0有双重的角色,可以表示整数零,也可以表示一个空指针。 在C语言中,通过预编译宏NULL,可以区分0表示的是零还是(void*)0. 但是,在C++的世界中,这样是不可以的。C++中允许函数重载。例如: ~~~ void foo(char *); void foo(int); ~~~ 如果把NULL定义为0,那么foo(NULL)将调用哪个函数呢? 这样的话,编译器不会知道调用哪个函数。 所以C++11引入了另一个关键字nullptr,作为一个空指针。 ~~~ char *pc = nullptr; // OK int *pi = nullptr; // OK bool b = nullptr; // OK. b is false. int i = nullptr; // error foo(nullptr); // calls foo(char *), not foo(int); ~~~
';

C++11新特性之 std::array container

最后更新于:2022-04-01 06:32:17

数组每个人都很熟悉,vector更是我们常常用到的。 但是某些场合,使用vector是多余的,尤其能明确元素个数的情况下,这样我们就付出了效率稍低的代价! 但是你使用数组的代价是那么的不安全,那么的不方便。 于是,C++11推出了模板类array,位于std名称控件中。  与vector不同的是,array对象的长度是固定的,使用了静态存储区,即存储在栈上,效率跟数组相同,但是更加的安全。 首先需要包含头文件array,而且语法与vector也有所不同: ~~~ #include<array> ... std::array<int,5> ai;//创建一个有5个int成员的对象 std::array<double,4> ad = {1.2, 2.1, 3.1, 4.1}; ~~~ 顺便提一嘴,C++11允许使用初始化列表对vetcor和array进行赋值,详情请见博客《 [c++11特性之initializer_list](http://blog.csdn.net/wangshubo1989/article/details/49622871 "初始化列表")》 个人觉得array相比于数组最大的优势就是可以将一个array对象赋值给另一个array对象: ~~~ std::array<double, 4> ad = {1.2, 2.1, 3.1, 4.1}; std::array<double, 4> ad1; ad1 = ad; ~~~ 数组索引越界也是我们往往躲不过的坑儿,array和vector使用中括号索引时也不会检查索引值的正确性。 但是,他们有一个成员函数可以替代中括号进行索引,这样越界就会进行检查: ~~~ std::array<double, 4> ad = {1.2, 2.1, 3.1, 4.1}; ad[-2] = 0.5;//合法 ad.at(1) = 1.1; ~~~ 使用at(),将在运行期间捕获非法索引的,默认将程序中断。 最后上一段代码: ~~~ #include <string> #include <iterator> #include <iostream> #include <algorithm> #include <array> int main() { // construction uses aggregate initialization std::array<int, 5> i_array1{ {3, 4, 5, 1, 2} }; // double-braces required std::array<int, 5> i_array2 = {1, 2, 3, 4, 5}; // except after = std::array<std::string, 2> string_array = { {std::string("a"), "b"} }; std::cout << "Initial i_array1 : "; for(auto i: i_array1) std::cout << i << ' '; // container operations are supported std::sort(i_array1.begin(), i_array1.end()); std::cout << "\nsored i_array1 : "; for(auto i: i_array1) std::cout << i << ' '; std::cout << "\nInitial i_array2 : "; for(auto i: i_array2) std::cout << i << ' '; std::cout << "\nreversed i_array2 : "; std::reverse_copy(i_array2.begin(), i_array2.end(), std::ostream_iterator<int>(std::cout, " ")); // ranged for loop is supported std::cout << "\nstring_array : "; for(auto& s: string_array) std::cout << s << ' '; return 0; } ~~~
';

C++11新特性之 CALLBACKS

最后更新于:2022-04-01 06:32:15

《C++11新特性之std::function》提到了std::function作为回调函数。 今天主要讨论不同情况下std::function作为回调使用。 **使用回调** ~~~ #include <functional> #include <iostream> namespace { using cb1_t = std::function<void()>; using cb2_t = std::function<void(int)>; void foo1() { std::cout << "foo1 is called\n"; } void foo2(int i) { std::cout << "foo2 is called with: " << i << "\n"; } struct S { void foo3() { std::cout << "foo3 is called.\n"; } }; } int main() { // Bind a free function. cb1_t f1 = std::bind(&foo1); // Invoke the function foo1. f1(); // Bind a free function with an int argument. // Note that the argument can be specified with bind directly. cb1_t f2 = std::bind(&foo2, 5); // Invoke the function foo2. f2(); // Bind a function with a placeholder. cb2_t f3 = std::bind(&foo2, std::placeholders::_1); // Invoke the function with an argument. f3(42); // Bind a member function. S s; cb1_t f4 = std::bind(&S::foo3, &s); // Invoke the method foo3. f4(); // Bind a lambda. cb1_t f5 = std::bind([] { std::cout << "lambda is called\n"; }); f5(); return 0; } ~~~ **存储回调**  使用回调是非常好的,但是更多的情况下,我们往往需要存储一些回调函数,并稍后使用。例如,注册一个客户端到某个事件上。(也许注册和事件是C Sharp的词汇) 我们经常使用std::vector来完成任务。  缺点就是,我们不能再vector中存储不同的类型 。  但是,大多数情况下,一种类型就可以满足我们了。 ~~~ #include <vector> #include <functional> #include <iostream> namespace { using cb1_t = std::function<void()>; using callbacks_t = std::vector<cb1_t>; callbacks_t callbacks; void foo1() { std::cout << "foo1 is called\n"; } void foo2(int i) { std::cout << "foo2 is called with: " << i << "\n"; } } // end anonymous namespace int main() { // Bind a free function. cb1_t f1 = std::bind(&foo1); callbacks.push_back(f1); // Bind a free function with an int argument. // Here the argument is statically known. cb1_t f2 = std::bind(&foo2, 5); callbacks.push_back(f2); // Bind a free function with an int argument. // Here the argument is bound and can be changed at runtime. int n = 15; cb1_t f3 = std::bind(&foo2, std::cref(n)); callbacks.push_back(f3); // Invoke the functions for(auto& fun : callbacks) { fun(); } return 0; } ~~~ **包装函数**  上面提到都不是重点,个人觉得特别重要的就是std::function作为函数的参数使用。  下面一个例子就将展示一个函数被copy不同的参数。 Note that we always produce an std::function, even though in some cases we could invoke the target directly. Whether this is required depends on the use case. If all the function does is invoking the target, then directly doing it is more efficient. The reason is that std::function does have some overhead, because it is a polymorphic class. ~~~ #include <functional> #include <iostream> namespace { using cb1_t = std::function<void()>; using cb2_t = std::function<void(int)>; // Wrapper function with std::function without arguments. template<typename R> void call(std::function<R(void)> f) { f(); } // Wrapper function with std::function with arguments. template<typename R, typename ...A> void call(std::function<R(A...)> f, A... args) { f(args...); } // Wrapper function for generic callable object without arguments. // Delegates to the std::function call. template<typename R> void call(R f(void)) { call(std::function<R(void)>(f)); } // Wrapper function for generic callable object with arguments. // Delegates to the std::function call. template<typename R, typename ...A> void call(R f(A...), A... args) { call(std::function<R(A...)>(f), args...); } // Wrapper for a function pointer (e.g. a lambda without capture) without // arguments. using fp = void (*)(void); void call(fp f) { call(std::function<void()>(f)); } void foo1() { std::cout << "foo1 is called\n"; } void foo2(int i) { std::cout << "foo2 is called with: " << i << "\n"; } } // end anonymous namespace int main() { // Call function 1. call(&foo1); // Alternative to call function 1. cb1_t f1 = std::bind(&foo1); call(f1); // Call function 2. call(&foo2, 5); // Alternative to call function 2. cb2_t f2 = std::bind(&foo2, std::placeholders::_1); call(f2, 5); // Here is an example with a lambda. It calls the function that takes a // function pointer. call([] { std::cout << "lambda called\n"; }); return 0; } ~~~
';

c++11特性之std::thread–进阶二

最后更新于:2022-04-01 06:32:13

继续C++11的std::thread之旅! 下面讨论如何**给线程传递参数**  这个例子是传递一个string ~~~ #include <iostream> #include <thread> #include <string> void thread_function(std::string s) { std::cout << "thread function "; std::cout << "message is = " << s << std::endl; } int main() { std::string s = "Kathy Perry"; std::thread t(&thread_function, s); std::cout << "main thread message = " << s << std::endl; t.join(); return 0; } ~~~ 如果运行,我们可以从输出结果看出传递成功了。 良好编程习惯的人都知道 传递引用的效率更高,那么我们该如何做呢?  你也许会这样写: ~~~ void thread_function(std::string &s) { std::cout << "thread function "; std::cout << "message is = " << s << std::endl; s = "Justin Beaver"; } ~~~ 为了确认是否传递了引用?我们在线程函数后更改这个参数,但是我们可以看到,输出结果并没有变化,即不是传递的引用。 事实上, 依然是按值传递而不是引用。为了达到目的,我们可以这么做: ~~~ std::thread t(&thread_function, std::ref(s)); ~~~ 这不是唯一的方法: 我们可以使用move(): ~~~ std::thread t(&thread_function, std::move(s)); ~~~ 接下来呢,我们就要将一下线程的复制吧!!  以下代码编译通过不了: ~~~ #include <iostream> #include <thread> void thread_function() { std::cout << "thread function\n"; } int main() { std::thread t(&thread_function); std::cout << "main thread\n"; std::thread t2 = t; t2.join(); return 0; } ~~~ 但是别着急,稍稍修改: ~~~ include <iostream> #include <thread> void thread_function() { std::cout << "thread function\n"; } int main() { std::thread t(&thread_function); std::cout << "main thread\n"; std::thread t2 = move(t); t2.join(); return 0; } ~~~ 大功告成!!! 再聊一个成员函数吧 **std::thread::get_id()**; ~~~ int main() { std::string s = "Kathy Perry"; std::thread t(&thread_function, std::move(s)); std::cout << "main thread message = " << s << std::endl; std::cout << "main thread id = " << std::this_thread::get_id() << std::endl; std::cout << "child thread id = " << t.get_id() << std::endl; t.join(); return 0; } Output: thread function message is = Kathy Perry main thread message = main thread id = 1208 child thread id = 5224 ~~~ 聊一聊**std::thread::hardware_concurrency()**  获得当前多少个线程: ~~~ int main() { std::cout << "Number of threads = " << std::thread::hardware_concurrency() << std::endl; return 0; } //输出: Number of threads = 2 ~~~ 之前介绍过c++11的**lambda表达式**  我们可以这样使用: ~~~ int main() { std::thread t([]() { std::cout << "thread function\n"; } ); std::cout << "main thread\n"; t.join(); // main thread waits for t to finish return 0; } ~~~
';

c++11特性之std::thread–进阶

最后更新于:2022-04-01 06:32:10

博客 `[c++11特性之std::thread--初识](http://blog.csdn.net/wangshubo1989/article/details/49592517 "std::thread")`  讲了std::thread::join和std::thread::detach的用法。今天就再来点深入的。 先看看这个: ~~~ int main() { std::thread t(&thread_function); std::cout << "main thread\n"; // t.join(); t.detach(); t.join(); // Error return 0; } ~~~ **一旦 detached, 就不能再join**  这个时候总要有新东西出场了:  **joinable()** ~~~ #include <iostream> // std::cout #include <thread> // std::thread void mythread() { // do stuff... } int main() { std::thread foo; std::thread bar(mythread); std::cout << "Joinable after construction:\n" << std::boolalpha; std::cout << "foo: " << foo.joinable() << '\n'; std::cout << "bar: " << bar.joinable() << '\n'; if (foo.joinable()) foo.join(); if (bar.joinable()) bar.join(); std::cout << "Joinable after joining:\n" << std::boolalpha; std::cout << "foo: " << foo.joinable() << '\n'; std::cout << "bar: " << bar.joinable() << '\n'; return 0; } ~~~ 之前的例子中, 我们都是使用的普通的函数. 我们本可以使用更多,尤其你要牢记的就是c++是一个面向对象。 ~~~ #include <iostream> #include <thread> class MyFunctor { public: void operator()() { std::cout << "functor\n"; } }; int main() { MyFunctor fnctor; std::thread t(fnctor); std::cout << "main thread\n"; t.join(); return 0; } ~~~ 上面的代码,我创建了一个函数对象,把它付给一个线程任务 也许你会像下面这样写: ~~~ std::thread t(MyFunctor()); ~~~ 但是,编译时通不过的,你应该这样写: ~~~ std::thread t((MyFunctor())); ~~~ **必须加一个 () 来封装 MyFunctor().**
';

c++11特性之initializer_list

最后更新于:2022-04-01 06:32:08

之前写了一个博客《 [浅析C++中的初始化列表(区别赋值和初始化)](http://blog.csdn.net/wangshubo1989/article/details/48937325 "成员变量初始化")》,讲述了类的构造函数使用初始化列表来初始化成员变量。 现在,撇开过往不谈,就谈一谈普通的变量赋值。即是我们要提到的initializer_list。 这同样是一个C++11的特性。 过往,我们这样给vector赋值: ~~~ std::vector v; v.push_back(1); v.push_back(2); v.push_back(3); v.push_back(4); ~~~ 需要感谢的是,C++11让你更方便。 ~~~ std::vector v = { 1, 2, 3, 4 }; ~~~ 这就是所谓的initializer list。 更进一步,有一个关键字叫initializer list C++11允许构造函数和其他函数把初始化列表当做参数。 ~~~ #include <iostream> #include <vector> class MyNumber { public: MyNumber(const std::initializer_list<int> &v) { for (auto itm : v) { mVec.push_back(itm); } } void print() { for (auto itm : mVec) { std::cout << itm << " "; } } private: std::vector<int> mVec; }; int main() { MyNumber m = { 1, 2, 3, 4 }; m.print(); // 1 2 3 4 return 0; } ~~~ 最后写一个类,可以对比一下,加深理解 ~~~ class CompareClass { CompareClass (int,int); CompareClass (initializer_list<int>); }; int main() { myclass foo {10,20}; // calls initializer_list ctor myclass bar (10,20); // calls first constructor } ~~~ 这下子明白 {} 和 () 的区别了吧!!!
';

c++11特性之std::thread–初识二

最后更新于:2022-04-01 06:32:06

上篇博客《[c++11特性之std::thread–初识](http://blog.csdn.net/wangshubo1989/article/details/49592517 "std::thread")》初步介绍了std::thread,并且介绍了几个成员函数。 最后的一段代码留了点悬念,就是vs2015会报错,错误如下: ~~~ error C2893: 未能使函数模板“unknown-type std::invoke(_Callable &&,_Types &&...)”专用化 1> d:\program files (x86)\microsoft visual studio 14.0\vc\include\thr\xthread(238): note: 用下列模板参数: ... ========== 生成: 成功 0 个,失败 1 个,最新 0 个,跳过 0 个 ========== ~~~ 代码改为: ~~~ #include <iostream> #include <thread> using namespace std; class Foo { void bar_i() { cout << "hello" << endl; } public: void bar() { auto func = std::bind(&Foo::bar_i, this); std::thread t(&Foo::bar_i, this); t.join(); } }; int main() { Foo f; f.bar(); } ~~~ 至于原因呢?  在std::thread中,指向成员函数的指针知道第一个参数是引用。 哈哈 上面一句话太牵强了,好吧 我也不是真正的理解 请大神指点迷津、
';

c++11特性之std::thread–初识

最后更新于:2022-04-01 06:32:03

C++11中已经拥有了一个更好用的用于线程操作的类`std::thread`。 默认构造函数:  thread() noexcept;  构造一个任何线程不执行的线程对象。 初始化函数: ~~~ template <class Fn, class... Args> explicit thread (Fn&& fn, Args&&... args); ~~~ 构造一个线程对象,可以开启线程执行  新执行的线程调用函数fn,并传递args作为参数  fn  可以指向函数,指向成员,或是移动构造函数  args…  传递给fn的参数,这些参数可以移动赋值构造。如果fn是一个成员指针,那么第一个args参数就必须是一个对象,或是引用,或是指向该对象的指针。 拷贝构造函数:  thread (const thread&) = delete;  删除构造的线程 现在介绍几个成员函数:  **std::thread::join**  该函数返回时,线程执行完成。  当 a thread 调用Join方法的时候,MainThread 就被停止执行,直到 a thread 线程执行完毕。 ~~~ #include <iostream> // std::cout #include <thread> // std::thread, std::this_thread::sleep_for #include <chrono> // std::chrono::seconds void pause_thread(int n) { std::this_thread::sleep_for (std::chrono::seconds(n)); std::cout << "pause of " << n << " seconds ended\n"; } int main() { std::cout << "Spawning 3 threads...\n"; std::thread t1 (pause_thread,1); std::thread t2 (pause_thread,2); std::thread t3 (pause_thread,3); std::cout << "Done spawning threads. Now waiting for them to join:\n"; t1.join(); t2.join(); t3.join(); std::cout << "All threads joined!\n"; return 0; } //输出 Output (after 3 seconds): Spawning 3 threads... Done spawning threads. Now waiting for them to join: pause of 1 seconds ended pause of 2 seconds ended pause of 3 seconds ended All threads joined! ~~~ **std::thread::detach**  分离线程的对象,使它们从彼此独立地执行所表示的线程。这两个线程继续没有阻止,也没有以任何方式同步。注意,当任一结束执行,其资源被释放。 ~~~ #include <iostream> // std::cout #include <thread> // std::thread, std::this_thread::sleep_for #include <chrono> // std::chrono::seconds void pause_thread(int n) { std::this_thread::sleep_for (std::chrono::seconds(n)); std::cout << "pause of " << n << " seconds ended\n"; } int main() { std::cout << "Spawning and detaching 3 threads...\n"; std::thread (pause_thread,1).detach(); std::thread (pause_thread,2).detach(); std::thread (pause_thread,3).detach(); std::cout << "Done spawning threads.\n"; std::cout << "(the main thread will now pause for 5 seconds)\n"; // give the detached threads time to finish (but not guaranteed!): pause_thread(5); return 0; } //输出 Output (after 5 seconds): Spawning and detaching 3 threads... Done spawning threads. (the main thread will now pause for 5 seconds) pause of 1 seconds ended pause of 2 seconds ended pause of 3 seconds ended pause of 5 seconds ended ~~~ 上一段代码 深入理解: ~~~ #include <iostream> #include <thread> using namespace std; class Foo { void bar_i() { cout << "hello" << endl; } public: void bar() { auto func = std::bind(&Foo::bar_i, this); std::thread t(&Foo::bar_i, std::ref(*this)); t.join(); } }; int main() { Foo f; f.bar(); } ~~~ 如果你使用的是VS2015,那么恭喜你,上面的代码你不会运行成功。
';

c++11特性之override和final关键字

最后更新于:2022-04-01 06:32:01

C++11之前,一直没有继承控制关键字。禁用一个类的进一步衍生是可能的但也很棘手。为避免用户在派生类中重载一个虚函数,你不得不向后考虑。 C++ 11添加了两个继承控制关键字:`override`和`final`。override确保在派生类中声明的重载函数跟基类的虚函数有相同的签名。final阻止类的进一步派生和虚函数的进一步重载。 **虚函数重载** 一个派生类可以重载在基类中声明的成员函数,这是面向对象设计的基础。然而像重载一个函数这么简单的操作也会出错。关于重载虚函数的两个常见错误如下:  **无意中重载**  **签名不匹配** 首先,我们来分析一下无意中重载的综合症。你可能只是通过声明了一个与基类的某个虚成员函数具有相同的名字和签名的成员函数而无意中重载了这个虚函数。编译器和读代码的人很难发现这个bug因为他们通常以为这个新函数是为了实现对基类函数的重载: ~~~ class A { public: virtual void func(); }; class B: A{}; class F{}; class D: A, F { public: void func();//meant to declare a new function but //accidentally overrides A::func}; ~~~ 阅读以上代码,你不能确定成员函数D::func()是否故意重载了`A::func()`.它也可能是个偶然发生的重载,因为两个函数的参数列表和名字都碰巧一样。 签名不匹配是一个更为常见的情景。这导致意外创建一个新的虚函数(而不是重载一个已存在的虚函数),正如以下例子所示: ~~~ class G { public: virtual void func(int); }; class H: G { public: virtual void func(double); }; ~~~ 这种情况下,程序员本打算在类H中重载`G::func()`的。然而,由于`H::func()`拥有不同的签名,结果创建了一个新的虚函数,而非对基类函数的重载: ~~~ H *p=new H; p->func(5); //calls G::f p->func(5.0); // calls H::f ~~~ 碰到这种情况,不是所有的编译器都会给个警告,有时那样做会被设置成抑制这种警告。 基于上面的两个错误 在C++11中,通过使用新关键字override可以消除这两个bugs。**override明确地表示一个函数是对基类中一个虚函数的重载**。更重要的是,它会检查基类虚函数和派生类中重载函数的签名不匹配问题。如果签名不匹配,编译器会发出错误信息。 我们来看看override如何消除签名不匹配bug的: ~~~ class G { public: virtual void func(int); }; class H: G { public: virtual void func(double) override; //compilation error }; ~~~ 当处理到H::func()声明时,编译器会 在一个基类查找与之匹配的虚函数。 **final函数和类** C++11的关键字final有两个用途。第一,它阻止了从类继承;第二,阻止一个虚函数的重载。我们先来看看final类吧。 程序员常常在没有意识到风险的情况下坚持从`std::vector`派生。在C++11中,无子类类型将被声明为如下所示: ~~~ class TaskManager {/*..*/} final; class PrioritizedTaskManager: public TaskManager { }; //compilation error: base class TaskManager is final ~~~ 同样,你可以通过声明它为final来禁止一个虚函数被进一步重载。如果一个派生类试图重载一个final函数,编译器就会报错: ~~~ class A { pulic: virtual void func() const; }; class B: A { pulic: void func() const override final; //OK }; class C: B { pulic: void func()const; //error, B::func is final }; ~~~ `C::func()`是否声明为override没关系,一旦一个虚函数被声明为final,派生类不能再重载它。
';

c++11特性之Lambda表达式

最后更新于:2022-04-01 06:31:59

“Lambda 表达式”(lambda expression)是一个匿名函数,`Lambda`表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。 我第一次接触`Lambda`表达式是在C#中,后来才知道C++11引入了`Lambda`表达式。 SO C++ 11 标准的一大亮点是引入`Lambda`表达式。基本语法如下:  **[capture list] (parameter list) ->return type { function body }** [] // 沒有定義任何變數。使用未定義變數會導致錯誤。  [x, &y] // x 以傳值方式傳入(預設),y 以傳參考方式傳入。  [&] // 任何被使用到的外部變數皆隱式地以參考方式加以引用。  [=] // 任何被使用到的外部變數皆隱式地以傳值方式加以引用。  [&, x] // x 顯示地以傳值方式加以引用。其餘變數以參考方式加以引用。  [=, &z] // z 顯示地以參考方式加以引用。其餘變數以傳值方式加以引用。 ~~~ class CTest { public: CTest() : m_nData(20) { NULL; } void TestLambda() { vector<int> vctTemp; vctTemp.push_back(1); vctTemp.push_back(2); // 无函数对象参数,输出:1 2 { for_each(vctTemp.begin(), vctTemp.end(), [](int v){ cout << v << endl; }); } // 以值方式传递作用域内所有可见的局部变量(包括this),输出:11 12 { int a = 10; for_each(vctTemp.begin(), vctTemp.end(), [=](int v){ cout << v+a << endl; }); } // 以引用方式传递作用域内所有可见的局部变量(包括this),输出:11 13 12 { int a = 10; for_each(vctTemp.begin(), vctTemp.end(), [&](int v)mutable{ cout << v+a << endl; a++; }); cout << a << endl; } // 以值方式传递局部变量a,输出:11 13 10 { int a = 10; for_each(vctTemp.begin(), vctTemp.end(), [a](int v)mutable{ cout << v+a << endl; a++; }); cout << a << endl; } // 以引用方式传递局部变量a,输出:11 13 12 { int a = 10; for_each(vctTemp.begin(), vctTemp.end(), [&a](int v){ cout << v+a << endl; a++; }); cout << a << endl; } // 传递this,输出:21 22 { for_each(vctTemp.begin(), vctTemp.end(), [this](int v){ cout << v+m_nData << endl; }); } // 除b按引用传递外,其他均按值传递,输出:11 12 17 { int a = 10; int b = 15; for_each(vctTemp.begin(), vctTemp.end(), [=, &b](int v){ cout << v+a << endl; b++; }); cout << b << endl; } // 操作符重载函数参数按引用传递,输出:2 3 { for_each(vctTemp.begin(), vctTemp.end(), [](int &v){ v++; }); for_each(vctTemp.begin(), vctTemp.end(), [](int v){ cout << v << endl; }); } // 空的Lambda表达式 { [](){}(); []{}(); } } private: int m_nData; }; ~~~ 其中除了“[ ]”(其中捕获列表可以为空)和“复合语句”(相当于具名函数定义的函数体),其它都是可选的。 C++中,一个`lambda`表达式表示一个可调用的代码单元。我们可以将其理解为一个未命名的内联函数。它与普通函数不同的是,`lambda`必须使用尾置返回来指定返回类型。 你可能会问,除了那些表达式爱好者,谁会用`lambda`表达式呢?  1、距离  程序员认为,让定义位于使用的地方附近很有用,因此这样无需翻阅多页的源代码。从这个角度看,`Lambda`表达式是理想的选择,因为定义和使用是在同一个地方。  2、简洁  函数符代码比函数和`Lambda`表达式更加繁琐,函数和`Lambda`表达式的简洁程度相当。但是不同于函数的是,`Lambda`可以定义于函数内部。  3、`lambda`可访问作用域内的任何动态变量。
';

c++11特性之正则表达式

最后更新于:2022-04-01 06:31:57

C++11中也将正则表达式纳入了新标准的一部分,不仅如此,它还支持了6种不同的正则表达式的语法,分别是:`ECMASCRIPT`、`basic`、`extended`、`awk`、`grep和egrep`。其中`ECMASCRIPT`是默认的语法,具体使用哪种语法我们可以在构造正则表达式的时候指定。 正则表达式库提供表示正则表达式,这是一种用于字符串内执行模式匹配小型的语言的类。 主要的类:  **basic_regex**  regular expression object **sub_match**  identifies the sequence of characters matched by a sub-expression **match_results**  identifies one regular expression match, including all sub-expression matches 算法:  These functions are used to apply the regular expression encapsulated in a regex to a target sequence of characters. **regex_match**  attempts to match a regular expression to an entire character sequence **regex_search**  attempts to match a regular expression to any part of a character sequence **regex_replace**  replaces occurrences of a regular expression with formatted replacement text 迭代器:  The regex iterators are used to traverse the entire set of regular expression matches found within a sequence. **regex_iterator**  iterates through all regex matches within a character sequence **regex_token_iterator**  iterates through the specified sub-expressions within all regex matches in a given string or through unmatched substrings 异常:  This class defines the type of objects thrown as exceptions to report errors from the regular expressions library. **regex_error**  reports errors generated by the regular expressions library 例子代码: ~~~ #include <iostream> #include <iterator> #include <string> #include <regex> int main() { std::string s = "Some people, when confronted with a problem, think " "\"I know, I'll use regular expressions.\" " "Now they have two problems."; std::regex self_regex("REGULAR EXPRESSIONS", std::regex_constants::ECMAScript | std::regex_constants::icase); if (std::regex_search(s, self_regex)) { std::cout << "Text contains the phrase 'regular expressions'\n"; } std::regex word_regex("(\\S+)"); auto words_begin = std::sregex_iterator(s.begin(), s.end(), word_regex); auto words_end = std::sregex_iterator(); std::cout << "Found " << std::distance(words_begin, words_end) << " words\n"; const int N = 6; std::cout << "Words longer than " << N << " characters:\n"; for (std::sregex_iterator i = words_begin; i != words_end; ++i) { std::smatch match = *i; std::string match_str = match.str(); if (match_str.size() > N) { std::cout << " " << match_str << '\n'; } } std::regex long_word_regex("(\\w{7,})"); std::string new_s = std::regex_replace(s, long_word_regex, "[$&]"); std::cout << new_s << '\n'; } Output: Text contains the phrase 'regular expressions' Found 19 words Words longer than 6 characters: people, confronted problem, regular expressions." problems. Some people, when [confronted] with a [problem], think "I know, I'll use [regular] [expressions]." Now they have two [problems]. ~~~
';

C++11新特性之std::function

最后更新于:2022-04-01 06:31:54

博客《[吐血整理C++11新特性](http://blog.csdn.net/wangshubo1989/article/details/48490035 "welcome")》描述了一些C++11带来的新的特性,但是不够全面。在实际工作中,用到了`std::function`来实现回调函数。所以写该博客做一个简要补充。 类模版`std::function`是一种通用、多态的函数封装。`std::function`的实例可以对任何可以调用的目标实体进行存储、复制、和调用操作,这些目标实体包括普通函数、`Lambda`表达式、函数指针、以及其它函数对象等。`std::function`对象是对C++中现有的可调用实体的一种类型安全的包裹(我们知道像函数指针这类可调用实体,是类型不安全的)。 通常`std::function`是一个函数对象类,它包装其它任意的函数对象,被包装的函数对象具有类型为T1, …,TN的N个参数,并且返回一个可转换到R类型的值。`std::function`使用 模板转换构造函数接收被包装的函数对象;特别是,闭包类型可以隐式地转换为`std::function`。 最简单的理解就是: 通过`std::function`对C++中各种可调用实体(普通函数、`Lambda`表达式、函数指针、以及其它函数对象等)的封装,形成一个新的可调用的`std::function`对象;让我们不再纠结那么多的可调用实体。 其中`Lambda`表达式也是C++11新增的内容。具体还不了解,应该类似于C Sharp中的`Lambda`表达式吧! ~~~ //代码出自链接:http://www.jellythink.com/archives/771 #include <functional> #include <iostream> using namespace std; std::function< int(int)> Functional; // 普通函数 int TestFunc(int a) { return a; } // Lambda表达式 auto lambda = [](int a)->int{ return a; }; // 仿函数(functor) class Functor { public: int operator()(int a) { return a; } }; // 1.类成员函数 // 2.类静态函数 class TestClass { public: int ClassMember(int a) { return a; } static int StaticMember(int a) { return a; } }; int main() { // 普通函数 Functional = TestFunc; int result = Functional(10); cout << "普通函数:"<< result << endl; // Lambda表达式 Functional = lambda; result = Functional(20); cout << "Lambda表达式:"<< result << endl; // 仿函数 Functor testFunctor; Functional = testFunctor; result = Functional(30); cout << "仿函数:"<< result << endl; // 类成员函数 TestClass testObj; Functional = std::bind(&TestClass::ClassMember, testObj, std::placeholders::_1); result = Functional(40); cout << "类成员函数:"<< result << endl; // 类静态函数 Functional = TestClass::StaticMember; result = Functional(50); cout << "类静态函数:"<< result << endl; return 0; } ~~~ 关于可调用实体转换为`std::function`对象需要遵守以下两条原则:  转换后的`std::function`对象的参数能转换为可调用实体的参数;  可调用实体的返回值能转换为`std::function`对象的返回值。  `std::function`对象最大的用处就是在**实现函数回调**(实际工作中就是用到了这一点),使用者需要注意,它不能被用来检查相等或者不相等,但是可以与`NULL`或者`nullptr`进行比较。
';

吐血整理C++11新特性

最后更新于:2022-04-01 06:31:52

本文整理一些C++11的新特性,欢迎补充。 **auto关键字**  在C++11之前,auto关键字用来指定存储期。在新标准中,它的功能变为类型推断。auto现在成了一个类型的占位符,通知编译器去根据初始化代码推断所声明变量的真实类型。各种作用域内声明变量都可以用到它。例如,名空间中,程序块中,或是for循环的初始化语句中。 ~~~ auto i = 42; // i is an int auto l = 42LL; // l is an long long auto p = new foo(); // p is a foo* ~~~ 使用auto通常意味着更短的代码(除非你所用类型是int,它会比auto少一个字母)。试想一下当你遍历STL容器时需要声明的那些迭代器(iterator)。现在不需要去声明那些typedef就可以得到简洁的代码了。 ~~~ std::map<std::string, std::vector<int>> map; for(auto it = begin(map); it != end(map); ++it) { } ~~~ 需要注意的是,auto不能用来声明函数的返回值。但**如果函数有一个尾随的返回类型时,auto是可以出现在函数声明中返回值位置**。这种情况下,auto并不是告诉编译器去推断返回类型,而是指引编译器去函数的末端寻找返回值类型。在下面这个例子中,函数的返回值类型就是operator+操作符作用在T1、T2类型变量上的返回值类型。 ~~~ template <typename T1, typename T2> auto compose(T1 t1, T2 t2) -> **decltype**(t1 + t2) { return t1+t2; } auto v = compose(2, 3.14); // v's type is double ~~~ **nullptr**  以前都是用0来表示空指针的,但由于0可以被隐式类型转换为整形,这就会存在一些问题。关键字nullptr是std::nullptr_t类型的值,用来指代空指针。nullptr和任何指针类型以及类成员指针类型的空值之间可以发生隐式类型转换,同样也可以隐式转换为bool型(取值为false)。但是不存在到整形的隐式类型转换。 ~~~ void foo(int* p) {} void bar(std::shared_ptr<int> p) {} int* p1 = NULL; int* p2 = nullptr; if(p1 == p2) { } foo(nullptr); bar(nullptr); bool f = nullptr; int i = nullptr; // error: A native nullptr can only be converted to bool or, using reinterpret_cast, to an integral type ~~~ 为了向前兼容,0仍然是个合法的空指针值。 **基于范围的for循环**  为了在遍历容器时支持”foreach”用法,C++11扩展了for语句的语法。用这个新的写法,可以遍历C类型的数组、初始化列表以及任何重载了非成员的begin()和end()函数的类型。  如果你只是想对集合或数组的每个元素做一些操作,而不关心下标、迭代器位置或者元素个数,那么这种foreach的for循环将会非常有用。 ~~~ std::map<std::string, std::vector<int>> map; std::vector<int> v; v.push_back(1); v.push_back(2); v.push_back(3); map["one"] = v; for(const auto& kvp : map) { std::cout << kvp.first << std::endl; for(auto v : kvp.second) { std::cout << v << std::endl; } } int arr[] = {1,2,3,4,5}; for(int& e : arr) { e = e*e; } ~~~ **Override和final**  看下面这个例子: ~~~ class B { public: virtual void f(short) {std::cout << "B::f" << std::endl;} }; class D : public B { public: virtual void f(int) {std::cout << "D::f" << std::endl;} }; ~~~ D::f 按理应当重写 B::f。然而二者的声明是不同的,一个参数是short,另一个是int。因此D::f只是拥有同样名字的另一个函数(重载)而不是重写。当你通过B类型的指针调用f()可能会期望打印出D::f,但实际上则会打出 B::f 。  另一个很微妙的错误情况:参数相同,但是基类的函数是const的,派生类的函数却不是。 ~~~ class B { public: virtual void f(int) const {std::cout << "B::f " << std::endl;} }; class D : public B { public: virtual void f(int) {std::cout << "D::f" << std::endl;} }; ~~~ 同样,这两个函数是重载而不是重写,所以你通过B类型指针调用f()将打印B::f,而不是D::f。  幸运的是,现在有一种方式能描述你的意图。新标准加入了两个新的标识符:  override,表示函数应当重写基类中的虚函数。  final,表示派生类不应当重写这个虚函数。  第一个的例子如下: ~~~ class B { public: virtual void f(short) {std::cout << "B::f" << std::endl;} }; class D : public B { public: virtual void f(int) override {std::cout << "D::f" << std::endl;} }; ~~~ 现在这将触发一个编译错误:  ‘D::f’ : method with override specifier ‘override’ did not override any base class methods  另一方面,如果你希望函数不要再被派生类进一步重写,你可以把它标识为final。可以在基类或任何派生类中使用final。在派生类中,可以同时使用override和final标识。 ~~~ class B { public: virtual void f(int) {std::cout << "B::f" << std::endl;} }; class D : public B { public: virtual void f(int) override final {std::cout << "D::f" << std::endl;} }; class F : public D { public: virtual void f(int) override {std::cout << "F::f" << std::endl;} }; ~~~ 被标记成final的函数将不能再被F::f重写。 **强类型枚举**  传统的C++枚举类型存在一些缺陷:它们会将枚举常量暴露在外层作用域中(这可能导致名字冲突,如果同一个作用域中存在两个不同的枚举类型,但是具有相同的枚举常量就会冲突),而且它们会被隐式转换为整形,无法拥有特定的用户定义类型。  在C++11中通过引入了一个称为**强类型枚举的新类型**,修正了这种情况。强类型枚举由关键字**enum class**标识。它不会将枚举常量暴露到外层作用域中,也不会隐式转换为整形,并且拥有用户指定的特定类型(传统枚举也增加了这个性质)。 ~~~ enum class Options {None, One, All}; Options o = Options::All; ~~~ **智能指针**  详见博客《[浅析C++中的智能指针](http://blog.csdn.net/wangshubo1989/article/details/48337955 "C++中的智能指针")》
';

前言

最后更新于:2022-04-01 06:31:50

> 原文出处:[浅析C++11新特性专栏文章](http://blog.csdn.net/column/details/newcpluspluseleven.html) > 作者:[王书博](http://blog.csdn.net/wangshubo1989) **本系列文章经作者授权在看云整理发布,未经作者允许,请勿转载!** # 浅析C++11新特性 > C++11包含了核心语言的新机能,并且拓展C++标准程序库,并且加入了大部分的C++ Technical Report 1程序库(数学上的特殊函数除外)。
';