C++11常用新特性汇总

2023-11-16

 

感谢博主的分享,转载自:http://www.cnblogs.com/feng-sc/p/5710724.html

 

C++11已经出来很久了,网上也早有很多优秀的C++11新特性的总结文章,在编写本博客之前,博主在工作和学习中学到的关于C++11方面的知识,也得益于很多其他网友的总结。本博客文章是在学习的基础上,加上博主在日常工作中的使用C++11的一些总结、经验和感悟,整理出来,分享给大家,希望对各位读者有帮助,文章中的总结可能存在很多不完整或有错误的地方,也希望读者指出。大家可以根据如下目录跳到自己需要的章节。

  1、关键字及新语法

    1.1、auto关键字及用法

    1.2、nullptr关键字及用法

    1.3、for循环语法

  2、STL容器

    2.1、std::array

    2.2、std::forward_list

    2.3、std::unordered_map

    2.4、std::unordered_set

  3、多线程

    3.1、std::thread

    3.2、st::atomic

    3.3、std::condition_variable

  4、智能指针内存管理

    4.1、std::shared_ptr

    4.2、std::weak_ptr

  5、其他

    5.1、std::function、std::bind封装可执行对象

    5.2、lamda表达式

1、关键字及新语法

  C++11相比C++98增加了许多关键字及新的语法特性,很多人觉得这些语法可有可无,没有新特性也可以用传统C++去实现。

  也许吧,但个人对待新技术总是抱着渴望而热衷的态度对待,也许正如很多人所想,用传统语法也可以实现,但新技术可以让你的设计更完美。这就如同在原来的维度里,你要完成一件事情,需要很多个复杂的步骤,但在新语法特性里,你可以从另外的维度,很干脆,直接就把原来很复杂的问题用很简单的方法解决了,我想着就是新的技术带来的一些编程体验上非常好的感觉。大家也不要觉得代码写得越复杂就先显得越牛B,有时候在理解的基础上,尽量选择“站在巨人的肩膀上”,可能你会站得更高,也看得更远。

  本章重点总结一些常用c++11新语法特点。后续会在本人理解的基础上,会继续在本博客内更新或增加新的小章节。

1.1、auto关键字及用法

  A、auto关键字能做什么?

  auto并没有让C++成为弱类型语言,也没有弱化变量什么,只是使用auto的时候,编译器根据上下文情况,确定auto变量的真正类型。

复制代码

//示例代码1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
auto AddTest(int a, int b) 
{
    return a + b;
}

int main()
{
    auto index = 10;
    auto str = "abc";
    auto ret = AddTest(1,2);
    std::cout << "index:" << index << std::endl;
    std::cout << "str:" << str << std::endl;
    std::cout << "res:" << ret << std::endl;
}

复制代码

  是的,你没看错,代码也没错,auto在C++14中可以作为函数的返回值,因此auto AddTest(int a, int b)的定义是没问题的。

  运行结果:

    

  B、auto不能做什么?

  auto作为函数返回值时,只能用于定义函数,不能用于声明函数。

复制代码

//Test.h 示例代码1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
#pragma once
class Test
{
public:
    auto TestWork(int a ,int b);
};

复制代码

  如下函数中,在引用头文件的调用TestWork函数是,编译无法通过。

  但如果把实现写在头文件中,可以编译通过,因为编译器可以根据函数实现的返回值确定auto的真实类型。如果读者用过inline类成员函数,这个应该很容易明白,此特性与inline类成员函数类似。

复制代码

//Test.h 示例代码1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
#pragma once
class Test
{
public:
    auto TestWork(int a, int b)
    {
        return a + b;
    }
};

复制代码

 

1.2、nullptr关键字及用法

  为什么需要nullptr? NULL有什么毛病?

  我们通过下面一个小小的例子来发现NULL的一点问题:

复制代码

//示例代码1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
class Test
{
public:
    void TestWork(int index)
    {
        std::cout << "TestWork 1" << std::endl;
    }
    void TestWork(int * index)
    {
        std::cout << "TestWork 2" << std::endl;
    }
};

int main()
{
    Test test;
    test.TestWork(NULL);
    test.TestWork(nullptr);
}

复制代码

  运行结果:

     

  NULL在c++里表示空指针,看到问题了吧,我们调用test.TestWork(NULL),其实期望是调用的是void TestWork(int * index),但结果调用了void TestWork(int index)。但使用nullptr的时候,我们能调用到正确的函数。

 

1.3、for循环语法

  习惯C#或java的同事之前使用C++的时候曾吐槽C++ for循环没有想C#那样foreach的用法,是的,在C++11之前,标准C++是无法做到的。熟悉boost库读者可能知道boost里面有foreach的宏定义BOOST_FOREACH,但个人觉得看起并不是那么美观。

  OK,我们直接以简单示例看看用法吧。

复制代码

//示例代码1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
int main()
{
    int numbers[] = { 1,2,3,4,5 };
    std::cout << "numbers:" << std::endl;
    for (auto number : numbers)
    {
        std::cout << number << std::endl;
    }
}

复制代码

   以上用法不仅仅局限于数据,STL容器都同样适用。

 

2、STL容器

  C++11在STL容器方面也有所增加,给人的感觉就是越来越完整,越来越丰富的感觉,可以让我们在不同场景下能选择跟具合适的容器,提高我们的效率。

  本章节总结C++11新增的一些容器,以及对其实现做一些简单的解释。

2.1、std::array

  个人觉得std::array跟数组并没有太大区别,对于多维数据使用std::array,个人反而有些不是很习惯吧。

  std::array相对于数组,增加了迭代器等函数(接口定义可参考C++官方文档)。

复制代码

//示例代码1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
#include <array>
int main()
{
    std::array<int, 4> arrayDemo = { 1,2,3,4 };
    std::cout << "arrayDemo:" << std::endl;
    for (auto itor : arrayDemo)
    {
        std::cout << itor << std::endl;
    }
    int arrayDemoSize = sizeof(arrayDemo);
    std::cout << "arrayDemo size:" << arrayDemoSize << std::endl;
    return 0;
}

复制代码

  运行结果:

     

  打印出来的size和直接使用数组定义结果是一样的。

 

2.2、std::forward_list

  std::forward_list为从++新增的线性表,与list区别在于它是单向链表。我们在学习数据结构的时候都知道,链表在对数据进行插入和删除是比顺序存储的线性表有优势,因此在插入和删除操作频繁的应用场景中,使用list和forward_list比使用array、vector和deque效率要高很多。

复制代码

//示例代码1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
#include <forward_list>
int main()
{
    std::forward_list<int> numbers = {1,2,3,4,5,4,4};
    std::cout << "numbers:" << std::endl;
    for (auto number : numbers)
    {
        std::cout << number << std::endl;
    }
    numbers.remove(4);
    std::cout << "numbers after remove:" << std::endl;
    for (auto number : numbers)
    {
        std::cout << number << std::endl;
    }
    return 0;
}

复制代码

  运行结果:

    

2.3、std::unordered_map

  std::unordered_map与std::map用法基本差不多,但STL在内部实现上有很大不同,std::map使用的数据结构为二叉树,而std::unordered_map内部是哈希表的实现方式,哈希map理论上查找效率为O(1)。但在存储效率上,哈希map需要增加哈希表的内存开销。

  下面代码为C++官网实例源码实例:

复制代码

//webset address: http://www.cplusplus.com/reference/unordered_map/unordered_map/bucket_count/
#include <iostream>
#include <string>
#include <unordered_map>
int main()
{
    std::unordered_map<std::string, std::string> mymap =
    {
        { "house","maison" },
        { "apple","pomme" },
        { "tree","arbre" },
        { "book","livre" },
        { "door","porte" },
        { "grapefruit","pamplemousse" }
    };
    unsigned n = mymap.bucket_count();
    std::cout << "mymap has " << n << " buckets.\n";
    for (unsigned i = 0; i<n; ++i) 
    {
        std::cout << "bucket #" << i << " contains: ";
        for (auto it = mymap.begin(i); it != mymap.end(i); ++it)
            std::cout << "[" << it->first << ":" << it->second << "] ";
        std::cout << "\n";
    }
    return 0;
}

复制代码

  运行结果:

    

  运行结果与官网给出的结果不一样。实验证明,不同编译器编译出来的结果不一样,如下为linux下gcc 4.6.3版本编译出来的结果。或许是因为使用的哈希算法不一样,个人没有深究此问题。

  

2.4、std::unordered_set

  std::unordered_set的数据存储结构也是哈希表的方式结构,除此之外,std::unordered_set在插入时不会自动排序,这都是std::set表现不同的地方。

  我们来测试一下下面的代码:  

复制代码

//示例代码1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
#include <iostream>
#include <string>
#include <unordered_set>
#include <set>
int main()
{
    std::unordered_set<int> unorder_set;
    unorder_set.insert(7);
    unorder_set.insert(5);
    unorder_set.insert(3);
    unorder_set.insert(4);
    unorder_set.insert(6);
    std::cout << "unorder_set:" << std::endl;
    for (auto itor : unorder_set)
    {
        std::cout << itor << std::endl;
    }

    std::set<int> set;
    set.insert(7);
    set.insert(5);
    set.insert(3);
    set.insert(4);
    set.insert(6);
    std::cout << "set:" << std::endl;
    for (auto itor : set)
    {
        std::cout << itor << std::endl;
    }
}

复制代码

  运行结果:

    

3、多线程

  在C++11以前,C++的多线程编程均需依赖系统或第三方接口实现,一定程度上影响了代码的移植性。C++11中,引入了boost库中的多线程部分内容,形成C++标准,形成标准后的boost多线程编程部分接口基本没有变化,这样方便了以前使用boost接口开发的使用者切换使用C++标准接口,把容易把boost接口升级为C++接口。

  我们通过如下几部分介绍C++11多线程方面的接口及使用方法。

3.1、std::thread

  std::thread为C++11的线程类,使用方法和boost接口一样,非常方便,同时,C++11的std::thread解决了boost::thread中构成参数限制的问题,我想着都是得益于C++11的可变参数的设计风格。

  我们通过如下代码熟悉下std::thread使用风格。

复制代码

//示例代码1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
#include <thread>
void threadfun1()
{
    std::cout << "threadfun1 - 1\r\n" << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << "threadfun1 - 2" << std::endl;
}

void threadfun2(int iParam, std::string sParam)
{
    std::cout << "threadfun2 - 1" << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(5));
    std::cout << "threadfun2 - 2" << std::endl;
}

int main()
{
    std::thread t1(threadfun1);
    std::thread t2(threadfun2, 10, "abc");
    t1.join();
    std::cout << "join" << std::endl;
    t2.detach();
    std::cout << "detach" << std::endl;
}

复制代码

  运行结果:

    

  有以上输出结果可以得知,t1.join()会等待t1线程退出后才继续往下执行,t2.detach()并不会并不会把,detach字符输出后,主函数退出,threadfun2还未执行完成,但是在主线程退出后,t2的线程也被已经被强退出。

 

3.2、std::atomic

  std::atomic为C++11分装的原子数据类型。

  什么是原子数据类型?

  从功能上看,简单地说,原子数据类型不会发生数据竞争,能直接用在多线程中而不必我们用户对其进行添加互斥资源锁的类型。从实现上,大家可以理解为这些原子类型内部自己加了锁。

  我们下面通过一个测试例子说明原子类型std::atomic_int的特点。

  下面例子中,我们使用10个线程,把std::atomic_int类型的变量iCount从100减到1。

复制代码

//示例代码1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
#include <thread>
#include <atomic>
#include <stdio.h>
std::atomic_bool bIsReady = false;
std::atomic_int iCount = 100;
void threadfun1()
{
    if (!bIsReady) {
        std::this_thread::yield();
    }
    while (iCount > 0)
    {
        printf("iCount:%d\r\n", iCount--);
    }
}

int main()
{
    std::atomic_bool b;
    std::list<std::thread> lstThread;
    for (int i = 0; i < 10; ++i)
    {
        lstThread.push_back(std::thread(threadfun1));
    }
    for (auto& th : lstThread)
    {
        th.join();
    }
}

复制代码

  运行结果:

  

   注:屏幕太短的原因,上面结果没有截完屏

  从上面的结果可以看到,iCount的最小结果都是1,单可能不是最后一次打印,没有小于等于0的情况,大家可以代码复制下来多运行几遍对比看看。

 

3.3、std::condition_variable

  C++11中的std::condition_variable就像Linux下使用pthread_cond_wait和pthread_cond_signal一样,可以让线程休眠,直到别唤醒,现在在从新执行。线程等待在多线程编程中使用非常频繁,经常需要等待一些异步执行的条件的返回结果。

  OK,在此不多解释,下面我们通过C++11官网的例子看看。

复制代码

 1 // webset address: http://www.cplusplus.com/reference/condition_variable/condition_variable/%20condition_variable
 2 // condition_variable example
 3 #include <iostream>           // std::cout
 4 #include <thread>             // std::thread
 5 #include <mutex>              // std::mutex, std::unique_lock
 6 #include <condition_variable> // std::condition_variable
 7 
 8 std::mutex mtx;
 9 std::condition_variable cv;
10 bool ready = false;
11 
12 void print_id(int id) {
13     std::unique_lock<std::mutex> lck(mtx);
14     while (!ready) cv.wait(lck);
15     // ...
16     std::cout << "thread " << id << '\n';
17 }
18 
19 void go() {
20     std::unique_lock<std::mutex> lck(mtx);
21     ready = true;
22     cv.notify_all();
23 }
24 
25 int main()
26 {
27     std::thread threads[10];
28     // spawn 10 threads:
29     for (int i = 0; i<10; ++i)
30         threads[i] = std::thread(print_id, i);
31 
32     std::cout << "10 threads ready to race...\n";
33     go();                       // go!
34 
35     for (auto& th : threads) th.join();
36 
37     return 0;
38 }

复制代码

  运行结果:

   

  上面的代码,在14行中调用cv.wait(lck)的时候,线程将进入休眠,在调用33行的go函数之前,10个线程都处于休眠状态,当22行的cv.notify_all()运行后,14行的休眠将结束,继续往下运行,最终输出如上结果。

 

4、智能指针内存管理

  在内存管理方面,C++11的std::auto_ptr基础上,移植了boost库中的智能指针的部分实现,如std::shared_ptr、std::weak_ptr等,当然,想boost::thread一样,C++11也修复了boost::make_shared中构造参数的限制问题。把智能指针添加为标准,个人觉得真的非常方便,毕竟在C++中,智能指针在编程设计中使用的还是非常广泛。

  什么是智能指针?网上已经有很多解释,个人觉得“智能指针”这个名词似乎起得过于“霸气”,很多初学者看到这个名词就觉得似乎很难。

  简单地说,智能指针只是用对象去管理一个资源指针,同时用一个计数器计算当前指针引用对象的个数,当管理指针的对象增加或减少时,计数器也相应加1或减1,当最后一个指针管理对象销毁时,计数器为1,此时在销毁指针管理对象的同时,也把指针管理对象所管理的指针进行delete操作。

  如下图所示,简单话了一下指针、智能指针对象和计数器之间的关系:

  

  

  下面的小章节中,我们分别介绍常用的两个智能指针std::shared_ptr、std::weak_ptr的用法。

4.1、std::shared_ptr

  std::shared_ptr包装了new操作符动态分别的内存,可以自由拷贝复制,基本上是使用最多的一个智能指针类型。

  我们通过下面例子来了解下std::shared_ptr的用法:

复制代码

//示例代码1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
#include <memory>
class Test
{
public:
    Test()
    {
        std::cout << "Test()" << std::endl;
    }
    ~Test()
    {
        std::cout << "~Test()" << std::endl;
    }
};
int main()
{
    std::shared_ptr<Test> p1 = std::make_shared<Test>();
    std::cout << "1 ref:" << p1.use_count() << std::endl;
    {
        std::shared_ptr<Test> p2 = p1;
        std::cout << "2 ref:" << p1.use_count() << std::endl;
    }
    std::cout << "3 ref:" << p1.use_count() << std::endl;
    return 0;
}

复制代码

  运行结果:

  

  从上面代码的运行结果,需要读者了解的是:

  1、std::make_shared封装了new方法,boost::make_shared之前的原则是既然释放资源delete由智能指针负责,那么应该把new封装起来,否则会让人觉得自己调用了new,但没有调用delete,似乎与谁申请,谁释放的原则不符。C++也沿用了这一做法。

  2、随着引用对象的增加std::shared_ptr<Test> p2 = p1,指针的引用计数有1变为2,当p2退出作用域后,p1的引用计数变回1,当main函数退出后,p1离开main函数的作用域,此时p1被销毁,当p1销毁时,检测到引用计数已经为1,就会在p1的析构函数中调用delete之前std::make_shared创建的指针。

4.2、std::weak_ptr

  std::weak_ptr网上很多人说其实是为了解决std::shared_ptr在相互引用的情况下出现的问题而存在的,C++官网对这个只能指针的解释也不多,那就先甭管那么多了,让我们暂时完全接受这个观点。

  std::weak_ptr有什么特点呢?与std::shared_ptr最大的差别是在赋值是,不会引起智能指针计数增加。

  我们下面将继续如下两点:

  1、std::shared_ptr相互引用会有什么后果;

  2、std::weak_ptr如何解决第一点的问题。

  A、std::shared_ptr相互引用的问题示例:

   

复制代码

//示例代码1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
#include <memory>
class TestB;
class TestA
{
public:
    TestA()
    {
        std::cout << "TestA()" << std::endl;
    }
    void ReferTestB(std::shared_ptr<TestB> test_ptr)
    {
        m_TestB_Ptr = test_ptr;
    }
    ~TestA()
    {
        std::cout << "~TestA()" << std::endl;
    }
private:
    std::shared_ptr<TestB> m_TestB_Ptr; //TestB的智能指针
}; 

class TestB
{
public:
    TestB()
    {
        std::cout << "TestB()" << std::endl;
    }

    void ReferTestB(std::shared_ptr<TestA> test_ptr)
    {
        m_TestA_Ptr = test_ptr;
    }
    ~TestB()
    {
        std::cout << "~TestB()" << std::endl;
    }
    std::shared_ptr<TestA> m_TestA_Ptr; //TestA的智能指针
};


int main()
{
    std::shared_ptr<TestA> ptr_a = std::make_shared<TestA>();
    std::shared_ptr<TestB> ptr_b = std::make_shared<TestB>();
    ptr_a->ReferTestB(ptr_b);
    ptr_b->ReferTestB(ptr_a);
    return 0;
}

复制代码

  运行结果:

    

  大家可以看到,上面代码中,我们创建了一个TestA和一个TestB的对象,但在整个main函数都运行完后,都没看到两个对象被析构,这是什么问题呢?

  原来,智能指针ptr_a中引用了ptr_b,同样ptr_b中也引用了ptr_a,在main函数退出前,ptr_a和ptr_b的引用计数均为2,退出main函数后,引用计数均变为1,也就是相互引用。

  这等效于说:

    ptr_a对ptr_b说,哎,我说ptr_b,我现在的条件是,你先释放我,我才能释放你,这是天生的,造物者决定的,改不了。

    ptr_b也对ptr_a说,我的条件也是一样,你先释放我,我才能释放你,怎么办?

  是吧,大家都没错,相互引用导致的问题就是释放条件的冲突,最终也可能导致内存泄漏。

 

  B、std::weak_ptr如何解决相互引用的问题

  我们在上面的代码基础上使用std::weak_ptr进行修改:

复制代码

//示例代码1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
#include <memory>
class TestB;
class TestA
{
public:
    TestA()
    {
        std::cout << "TestA()" << std::endl;
    }
    void ReferTestB(std::shared_ptr<TestB> test_ptr)
    {
        m_TestB_Ptr = test_ptr;
    }
    void TestWork()
    {
        std::cout << "~TestA::TestWork()" << std::endl;
    }
    ~TestA()
    {
        std::cout << "~TestA()" << std::endl;
    }
private:
    std::weak_ptr<TestB> m_TestB_Ptr;
};

class TestB
{
public:
    TestB()
    {
        std::cout << "TestB()" << std::endl;
    }

    void ReferTestB(std::shared_ptr<TestA> test_ptr)
    {
        m_TestA_Ptr = test_ptr;
    }
    void TestWork()
    {
        std::cout << "~TestB::TestWork()" << std::endl;
    }
    ~TestB()
    {
        std::shared_ptr<TestA> tmp = m_TestA_Ptr.lock();
        tmp->TestWork();
        std::cout << "2 ref a:" << tmp.use_count() << std::endl;
        std::cout << "~TestB()" << std::endl;
    }
    std::weak_ptr<TestA> m_TestA_Ptr;
};


int main()
{
    std::shared_ptr<TestA> ptr_a = std::make_shared<TestA>();
    std::shared_ptr<TestB> ptr_b = std::make_shared<TestB>();
    ptr_a->ReferTestB(ptr_b);
    ptr_b->ReferTestB(ptr_a);
    std::cout << "1 ref a:" << ptr_a.use_count() << std::endl;
    std::cout << "1 ref b:" << ptr_a.use_count() << std::endl;
    return 0;
}

复制代码

  运行结果:

      

  由以上代码运行结果我们可以看到:

  1、所有的对象最后都能正常释放,不会存在上一个例子中的内存没有释放的问题。

  2、ptr_a 和ptr_b在main函数中退出前,引用计数均为1,也就是说,在TestA和TestB中对std::weak_ptr的相互引用,不会导致计数的增加。在TestB析构函数中,调用std::shared_ptr<TestA> tmp = m_TestA_Ptr.lock(),把std::weak_ptr类型转换成std::shared_ptr类型,然后对TestA对象进行调用。

  

5、其他

  本章节介绍的内容如果按照分类来看,也属于以上语法类别,但感觉还是单独拿出来总结好些。

  下面小节主要介绍std::function、std::bind和lamda表达式的一些特点和用法,希望对读者能有所帮助。

5.1、std::function、std::bind封装可执行对象

  std::bind和std::function也是从boost中移植进来的C++新标准,这两个语法使得封装可执行对象变得简单而易用。此外,std::bind和std::function也可以结合我们一下所说的lamda表达式一起使用,使得可执行对象的写法更加“花俏”。

  我们下面通过实例一步步了解std::function和std::bind的用法:

  Test.h文件

复制代码

//Test.h 示例代码1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
class Test
{
public:
    void Add()
    {
        
    }
};

复制代码

  main.cpp文件

复制代码

//main.cpp 示例代码1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
#include <functional>
#include <iostream>
#include "Test.h"
int add(int a,int b)
{
    return a + b;
}

int main()
{
    Test test;
    test.Add();
    return 0;
}

复制代码

  解释:

    上面代码中,我们实现了一个add函数和一个Test类,Test类里面有一个Test函数也有一个函数Add。

 

  OK,我们现在来考虑一下这个问题,假如我们的需求是让Test里面的Add由外部实现,如main.cpp里面的add函数,有什么方法呢?

  没错,我们可以用函数指针。

  我们修改Test.h

复制代码

//示例代码1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
class Test
{
public:
    typedef int(*FunType)(int, int);
    void Add(FunType fun,int a,int b)
    {
        int sum = fun(a, b);
        std::cout << "sum:" << sum << std::endl;
    }
};

复制代码

  修改main.cpp的调用

复制代码

//示例代码1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
....
....
Test test;
test.Add(add, 1, 2);
....

复制代码

  运行结果:

    

 

  到现在为止,完美了吗?如果你是Test.h的提供者,你觉得有什么问题?

  我们把问题升级,假如add实现是在另外一个类内部,如下代码:

复制代码

//示例代码1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
class TestAdd
{
public:
    int Add(int a,int b)
    {
        return a + b;
    }
};

int main()
{
    Test test;
    //test.Add(add, 1, 2);
    return 0;
}

复制代码

  假如add方法在TestAdd类内部,那你的Test类没辙了,因为Test里的Test函数只接受函数指针。你可能说,这个不是我的问题啊,我是接口的定义者,使用者应该遵循我的规则。但如果现在我是客户,我们谈一笔生意,就是我要购买使用你的Test类,前提是需要支持我传入函数指针,也能传入对象函数,你做不做这笔生意?

  是的,你可以选择不做这笔生意。我们现在再假设你已经好几个月没吃肉了(别跟我说你是素食主义者),身边的苍蝇肉、蚊子肉啊都不被你吃光了,好不容易等到有机会吃肉,那有什么办法呢?

  这个时候std::function和std::bind就帮上忙了。

  我们继续修改代码:

  Test.h

复制代码

//示例代码1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
class Test
{
public:
    void Add(std::function<int(int, int)> fun, int a, int b)
    {
        int sum = fun(a, b);
        std::cout << "sum:" << sum << std::endl;
    }
};

复制代码

  解释:

    Test类中std::function<int(int,int)>表示std::function封装的可执行对象返回值和两个参数均为int类型。

  main.cpp

复制代码

//示例代码1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
int add(int a,int b)
{
    std::cout << "add" << std::endl;
    return a + b;
}

class TestAdd
{
public:
    int Add(int a,int b)
    {
        std::cout << "TestAdd::Add" << std::endl;
        return a + b;
    }
};

int main()
{
    Test test;
    test.Add(add, 1, 2);

    TestAdd testAdd;
    test.Add(std::bind(&TestAdd::Add, testAdd, std::placeholders::_1, std::placeholders::_2), 1, 2);
    return 0;
}

复制代码

  解释:

    std::bind第一个参数为对象函数指针,表示函数相对于类的首地址的偏移量;

    testAdd为对象指针;

    std::placeholders::_1和std::placeholders::_2为参数占位符,表示std::bind封装的可执行对象可以接受两个参数。

  

  运行结果:

  

  是的,得出这个结果,你就可以等着吃肉了,我们的Test函数在函数指针和类对象函数中都两种情况下都完美运行。

  

5.2、lamda表达式

  在众多的C++11新特性中,个人觉得lamda表达式不仅仅是一个语法新特性,对于没有用过java或C#lamda表达式读者,C++11的lamda表达式在一定程度上还冲击着你对传统C++编程的思维和想法。

  我们先从一个简单的例子来看看lamda表达式:

复制代码

//示例代码1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
1 int main()
2 {
3     auto add= [](int a, int b)->int{
4         return a + b;
5     };
6     int ret = add(1,2);
7     std::cout << "ret:" << ret << std::endl;
8     return 0;
9 }

复制代码

  解释:

    第3至5行为lamda表达式的定义部分

    []:中括号用于控制main函数与内,lamda表达式之前的变量在lamda表达式中的访问形式;

    (int a,int b):为函数的形参

    ->int:lamda表达式函数的返回值定义

    {}:大括号内为lamda表达式的函数体。

  运行结果:

  

  我使用lamda表达式修改5.1中的例子看看:

  main.cpp

复制代码

//示例代码1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
.....

int main()
{
    Test test;
    test.Add(add, 1, 2);

    TestAdd testAdd;
    test.Add(std::bind(&TestAdd::Add, testAdd, std::placeholders::_1, std::placeholders::_2), 1, 2);

    test.Add([](int a, int b)->int {
        std::cout << "lamda add fun" << std::endl;
        return a + b;
    },1,2);
    return 0;
}

复制代码

  运行结果:

    

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++11常用新特性汇总 的相关文章

  • 华为UOS欧拉版 K3S+Rancher 安装完全版

    文章目录 K3S服务 happy path安装过程 1 准备工作 1 1 修改网卡名称为eth0 1 2 切换yum源 1 3 关闭防火墙以及selinux 1 4 修改主机名 并修改hosts 1 5 在UOS 基于华为欧拉 上安装doc
  • 多线程实现大批量数据查询

    优化一个系统中的功能 需要通过判断进行多次的查库 查库的性能是单表 条件有索引 public Map
  • chatglm微调

    chatGML 看到 官方教程 ChatGLM 6B 微调 P Tuning LoRA Full parameter 精准空降到 15 27 https www bilibili com video BV1fd4y1Z7Y5 share s
  • 【自定义表单】自定义表单设计

    1 后端设计1 diy field pool 字段池 我们定义好的字段类型 diy form 表单表 记录用户自定义的表单 diy form field 表单字段表 记录某张表单中有哪些字段 diy form entity 表单实例表 记录
  • matplotlib - 确保 0 在 RdBu 颜色条中变为白色

    matplotlib 确保 0 在 RdBu 颜色条中变为白色 matplotlib 确保 0 在 RdBu 颜色条中变为白色 IT工具网 标签 matplotlib colormap 我使用以下代码段创建了一个热图 span style
  • linux离线部署python环境

    在实际生产中 经常需要离线在服务器上部署python环境 第一步 安装python环境 选择安装miniconda3作为python环境 下载Miniconda3 latest Linux x86 64 sh 之后安装即可 习惯将路径保存为
  • pyecharts 安装报错 ModuleNotFoundError: No module named ‘pyecharts_snapshot‘

    1 出错原因 因为用下面语句安装pyecharts时 默认会安装最新版本的pyecharts python解释器版本更新的速度慢很多 现在的python解释器默认的是与0 1 9 4版本的pyecharts配合 你安装最新的 python解
  • 对大学生活的几点建议

    List item 在大学不要对人随意的掏心掏肺 因为你的痛苦只有你自己知道 其他人没有经历过 不会懂 也许你掏心掏肺的说一大通 在他人眼中就是幼稚 就是不成熟 我还记得自己刚上大学 因为自卑 几乎没和其他人有来往 现在虽比起以前强了 但是
  • git 远程删除不需要的文件

    git clone git 192 168 2 246 dev ncrm git 23 find name target 24 find name target xargs rm rf 25 find name target 26 git
  • Ai&Bd资料

    Ai Bd资料收集 目录 Ai Bd资料收集 1 1 人工智能AI 1 1 1 产业规模 头部企业占比 1 1 1 1 总体产业规模 1 1 1 2 投资热点 2 1 1 3头部企业占比 3 1 2 技术流派 4 1 2 研判未来3 5年产
  • 怎么让VSCode标签栏只展示一个窗口

    怎么让VSCode标签栏只展示一个窗口 问题 解决方法 参考 问题 我们希望VSCode窗口的标签栏是这么分开显示标签的 但是 偶尔重启该应用 会突然发现变成了只显示一个标签 不会再分开展示了 变成了一个文件名加路径了 这种情况下原来的标签
  • 随笔篇-多线程世界的来龙去脉

    文章目录 线程 多线程带来的问题 线程的挂起与唤醒 线程的管理 如果看官觉得有点用 点赞一下 鼓励一下我吧 感谢原创的整理 以下是原文作者连接 原文 https zhuanlan zhihu com p 122010626 以下为摘抄概要整
  • C++11常用新特性汇总

    感谢博主的分享 转载自 http www cnblogs com feng sc p 5710724 html C 11已经出来很久了 网上也早有很多优秀的C 11新特性的总结文章 在编写本博客之前 博主在工作和学习中学到的关于C 11方面
  • 随笔之---java版本哲学家就餐问题【信号量的实现】

    很喜欢这样的描述如果你喜欢也不防读一读 从许多许多年前 石头就呆在那座岭上了 那是座无名的低岭 毫不起眼 没有足以称道的风景名胜 那块石头只是许多石头中的一颗 见证过日升日落 经历过沧海桑田 承受四季变迁 黄河水数度从它的身上淹没而过 人群
  • PDF文件转化成mobi格式,亲测kindle或者iReader可用!

    convertfiles 点击连接 然后选择要转换的文件 比如我的是MySQL的 选择输入文件和输出文件的格式 转换 对了记得输入邮箱号码 转化完毕会发送连接到邮箱提供下载 或者 网络流畅的情况下转化完毕会自动重定向到下载页面
  • 瞎写

    有人说人生有两大禁忌 一忌踌躇满志 一忌心灰意冷 别人我不知道 但是对我来说 似乎一直都在这两种情绪之间跳转 说实话写这篇文章的此时我应该是处于心灰意冷这个点的 下面就随便说说当处于这个点时 自己产生的一些想法 首先 处于这个状态时整个人肯
  • 人生之路漫长

    人生不同的时间会遇到不同的人和事 你不知道这些事会对你以后产生什么影响 只希望后面有好的收获
  • Spring注解开发

    Spring配置繁重 注解提高开发速度 在applicationContext xml中配置组件扫描 作用是指定哪个包及其子包下的Bean需要进行扫描以便识别使用注解配置的类 字段和方法
  • 前端vue项目部署到tomcat,一刷新报错404解决方法

    原文链接 https my oschina net u 1471354 blog 4277008 VUE项目部署到Tomcat之后 刷新页面会出现404 此问题主要是使用了VUE router的History模式 一 解决方案 1 编辑se
  • 集合的初始容量与扩容

    arraylist 0 50 linkedlist 0 链表每次加一 hashmap 16 加载因子0 75 超过容量的0 75才会进行扩容 扩容 容量的2倍 2 当某个桶的链表长度达到8 就转成红黑树

随机推荐

  • Linux修改文件所有者和用户组

    一 修改文件所有者 修改的用户必须在 etc passwd文件中 etc passwd记录用户信息 chown change owner的简写 修改文件的所有者 chown R 所有者名称 文件或目录 R 递归 将子目录下文件全部修改 二
  • 关于 Vulkan 简介 —— Android N 引入新的 3D 渲染引擎

    关于 Vulkan Vulkan 被视作是 OpenGL 的后续产品 它是一种多平台 API 可支持开发人员准备游戏 CAD 工具 性能基准测试等高性能图形应用 它可在不同的操作系统 比如 Windows Linux 或 Android 上
  • Interface中input delay&output delay

    最开始在学习SV的时候 碰到interface的使用并没有过多的在意 只是了解clocking block是为了解决竞争问题 然而在后续使用clocking block的过程中 总会碰到一些时序错位的问题 如下 通过简单的例子来表述下clo
  • Mac 安装 Android Studio 以及 Android 开发环境配置

    安装 Android Studio 1 下载 Android Studio 下载地址 https developer android google cn studio 直接下载最新的dmg文件 安装 一直点 Next 下一步直到完成 2 P
  • 多线程日志库

    本文参考自陈硕老师的muduo网络库 删减了繁多的依赖文件 精简如此 供更多的人参考学习 感谢陈硕老师 copyable h ifndef MUDUO BASE COPYABLE H define MUDUO BASE COPYABLE H
  • 小小换行符乱谈(文本文件vs二进制文件)

    使用 C 语言的 fopen 打开文件时 可以指定的 mode 有 12 个 其中 6 个包含 b 使用 C 的 fstream 打开文件时 可用的模式组合有 24 个 其中 12 个包含 binary 使用 python 的 open 打
  • Unity3D 4.0 界面 基础 入门

    Unity3D 4 0 界面 基础 入门 一 屏幕布局 2 by 3布局 4Split布局 Tall布局 高的布局 wide布局 二 在屏幕布局模式下的五个主要区域 1 场景视图 Scene View 用于摆放游戏对象 2 游戏视图 Gam
  • CMOS图像传感器OV7740数据手册

    下载地址 阿里云盘分享https www aliyundrive com s 4GXdCkz9mvG
  • git从已有分支拉一个自己的开发分支

    第一步 切换到被copy分支 并且当前分支必须要保持是最新代码 git checkout 被copy分支 git pull 第二步 从当前分支拉开发分支 git checkout b 自己的分支名称 如下 tengxiao ma SJ DN
  • 微信小程序的常用组件

    目录 一 常用的视图容器类组件 view scroll view swiper 和 swiper item 二 常用的基础内容组件 text rich text 三 其它常用组件 button image navigator 一 常用的视图
  • 启动tomcat服务器,为何要配置CATALINA_HOME和JAVA_HOME ?

    问题 win10系统 本地安装jdk 配置环境变量 是将jdk的bin目录 笔者本地目录为 E JavaTools jdk1 8 0 131 bin 直接配置到系统变量path中 cmd执行java javac都正常 认为jdk安装配置没有
  • vue中运行项目自动打开浏览器失败

    问题 在package json文件中设置 open后自动打开失败 失败 失败后跳转到浏览器中 解决方法 在vue config js中将原先的代码替换成如下代码并保存后 重新编译就ok了 const defineConfig requir
  • 2018-CVPR-NVIDIA-Super SloMo: High Quality Estimation of Multiple Intermediate Frames for Video Inte

    基于光流反向变换的框架 第一部分是双向光流估计 第二部分是进行中间帧的合成 采用了stacking的思想 将光流的估计分成两个阶段 第一阶段是粗估计 第二阶段再进行精调 从而来改善图像的生成效果 此外第二阶段还要估计出掩膜权重 参考 htt
  • ubuntu初次使用笔记

    环境 win7 vmware10 ubuntu13 10 1 上网配置 一般只要装上虚拟机 安装ubuntu之后 选择桥接模式联网即可 但是 也有可能出现奇怪的问题 那么看以下设置 假设ubuntu联网方式设置为NAT NAT和桥接模式的区
  • IT自由职业者的成功秘诀

    原文作者Greg Jorgensen是一位典型的程序员 他从1974年开始编程 曾在耐克和苹果等公司任职 他专攻修复和完善受损 被遗弃和 半生不熟 的Web应用程序 尤其是后台语言是PHP的网站 我从事自由职业已有十余年了 有时候在我有全职
  • Python 日期、时间处理、时间戳转换、获取年份、月份、日、星期几、小时、分钟、秒

    引入 time 模块 import time 获取当前时间戳 unix timestamp current time time print unix timestamp current 1596594152 331776 格式化时间 fmt
  • Uncaught (in promise) TypeError: Cannot set properties of undefined (setting ‘type‘) at main_Mes

    vue项目报错解决办法 删掉 Message 就好了
  • 内网通过计算机名查询IP地址

    计算机环境 win10 内网 已知计算机名为 DESKTOP 40BB7CS 查询计算机IP地址 nbtstat a DESKTOP 40BB7CS 结果 以太网 节点 IP 址址 10 9 54 37 范围 ID NetBIOS 远程计算
  • Latex Picture And Table Setting

    Four Picture in one column begin figure htb begin minipage b 48 linewidth centering centerline includegraphics width 4 0
  • C++11常用新特性汇总

    感谢博主的分享 转载自 http www cnblogs com feng sc p 5710724 html C 11已经出来很久了 网上也早有很多优秀的C 11新特性的总结文章 在编写本博客之前 博主在工作和学习中学到的关于C 11方面