C++11 玩家不得不学的语法集 [持续更新-建议收藏]

本文详细介绍了C++中std::function、std::bind、std::ref、std::thread和std::packaged_task等特性,展示了它们在函数式编程、函数调用绑定、引用传递、多线程以及异步操作中的应用,同时提及了placementnew用于内存管理的特殊用法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.std::function 开启函数式编程之路

std::function 是 C++ 标准库中的一个模板类,用于封装可调用对象(函数、函数对象、成员函数指针等),并提供一种统一的调用接口。它的主要作用是将可调用对象抽象为一个对象,使得可以像普通对象一样进行传递、存储和调用,从而提高了 C++ 中函数的灵活性和可重用性。

代码:

#include <iostream>
#include <functional>

// 函数对象示例
struct Functor {
    int operator()(int a, int b) const {
        return a + b;
    }
};

// 普通函数示例
int add(int a, int b) {
    return a + b;
}

// 成员函数示例
class MyClass {
public:
    int addMember(int &a, int b) const {
        return a + b;
    }
};

int main() {
    // 绑定普通函数
    std::function<int(int, int)> func1 = add;
    std::cout << "func1 result: " << func1(1, 2) << std::endl;

    // 绑定函数对象
    Functor functor;
    std::function<int(int, int)> func2 = functor;
    std::cout << "func2 result: " << func2(3, 4) << std::endl;

    // 绑定 lambda 表达式
    std::function<int(int, int)> func3 = [](int a, int b) -> int { return a + b; };
    std::cout << "func3 result: " << func3(5, 6) << std::endl;

    // 绑定成员函数
    MyClass obj;int a = 100;
    std::function<int(const MyClass&, int&, int)> func4 = &MyClass::addMember;
    a = 1000;
    std::cout << "func4 result: " << func4(obj, std::ref(a), 8) << std::endl;

    // 使用 std::placeholders 绑定成员函数
    std::function<int(const MyClass&, int&, int)> func5 = std::bind(&MyClass::addMember, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
    std::cout << "func5 result: " << func5(obj, a, 10) << std::endl;

    // 使用 std::ref 传递引用
    int result = 0;
    std::function<void(int, int)> func6 = [&result](int a, int b) { result = a + b; };
    func6(11, 12);
    std::cout << "func6 result: " << result << std::endl;

    return 0;
}

2.std::bind 灵活处理函数调用和传参

std::bind 是 C++ 标准库中的一个函数模板,用于将函数与其参数绑定起来,生成一个可调用对象。它的主要作用是延迟函数调用,并且可以在调用时提供部分或全部的参数。std::bind 可以用于创建函数对象、绑定成员函数、创建函数适配器等。

代码:

#include <iostream>
#include <functional>

// 函数对象示例
int add(int a, int b) {
    return a + b;
}

// 成员函数示例
class MyClass {
public:
    int addMember(int a, int b) {
        return a + b;
    }
};

int main() {
    int a = 1, b = 2 ;

    // 绑定普通函数
    auto func1 = std::bind(add, std::ref(a), std::ref(b)); // std::ref 见 std::ref 对应节
    b = a = 111;
    std::cout << "func1 result: " << func1() << std::endl;

    // 绑定成员函数
    MyClass obj;
    auto func2 = std::bind(&MyClass::addMember, &obj, std::ref(a), std::ref(b));
    a=b=1;
    std::cout << "func2 result: " << func2() << std::endl;

    // 绑定成员函数并传递额外参数
    auto func3 = std::bind(&MyClass::addMember, &obj, std::placeholders::_1, std::placeholders::_2);
    std::cout << "func3 result: " << func3(a, b) << std::endl;

    // 绑定成员函数的成员
    auto func4 = std::bind(&MyClass::addMember, &obj, std::placeholders::_1, std::ref(b));
    b = 2;
    std::cout << "func4 result: " << func4(a) << std::endl;

    return 0;
}

3. std::ref 传递引用的必要包装

std::ref 是 C++ 标准库中的一个函数模板,用于将一个对象或者引用包装为一个 std::reference_wrapper 对象,从而使得对象可以像引用一样被传递。std::reference_wrapper 对象可以在函数传参时传递引用,而不是对象的副本,这在某些情况下可以提高程序的效率。诸如:std::bind、std::function

还是不理解的话,举例如下:std::thread 在传入参数时默认为值传递,此时,对要传递的左值使用std::ref(variable) 则可以传递引用进去。示例可见 std::thread。

代码

#include <iostream>
#include <functional>

void modify(int& num) {
    num += 10;
}

int main() {
    int num = 5;

    // 通过 std::ref 将引用包装成 std::reference_wrapper
    auto refNum = std::ref(num);

    // 通过 std::reference_wrapper 传递引用
    modify(refNum);

    // 输出修改后的值
    std::cout << "Modified num: " << num << std::endl;

    return 0;
}

4.std::thread 标准库的多线程

std::thread 是 C++ 标准库中用于创建线程的类,它提供了一种方便的方式来创建并发执行的线程。

code ①:常规各类用法示例

#include <iostream>
#include <thread>
#include <vector>

// 线程函数
void threadFunction(int& num) {
    ++num;
    std::cout << "Hello from thread! Num: " << num << std::endl;
}

class Worker {
public:
    void operator()() const {
        std::cout << "Hello from worker thread!" << std::endl;
    }
};

int main() {
    int Num = 1;

    // 创建一个线程并启动
    //std::thread t1(threadFunction, Num ); 编译报错
    //t1.join();
    std::thread t1_ref(threadFunction, std::ref(Num) );
    t1_ref.join();
    std::cout << "Hello from Main thread! Num: " << Num << std::endl;

    // 创建一个线程并启动,使用 Lambda 表达式
    int val = 2;
    std::thread t2([val](){
        std::cout << "Hello from thread! Value: " << val << std::endl;
    });

    // 创建一个线程并启动,使用函数对象
    Worker worker;
    std::thread t3(worker);

    // 等待所有线程结束
    t2.join();
    t3.join();

    // 创建多个线程并启动
    std::vector<std::thread> threads;
    for (int i = 0; i < 5; ++i) {
        threads.emplace_back([i]() {
            std::cout << "Hello from thread " << i << std::endl;
        });
    }

    // 等待所有线程结束
    for (auto& thread : threads) {
        thread.join();
    }

    // 获取硬件线程数
    unsigned int numThreads = std::thread::hardware_concurrency();
    std::cout << "Number of hardware threads: " << numThreads << std::endl;

    return 0;
}

code ②:类成员重载时需要转换指针类型,否则报错(std::bind也有同类问题)

#include <iostream>
#include <thread>
#include <string>

class A {
public:
    void setName(const std::string& name) {
        this->name = name;
    }

    void funcDefault(int a) {
        std::cout << "func4(int) called with value: " << a << " and name: " << name << std::endl;
    }

    // Overload for int argument
    void func(int a) {
        std::cout << "func4(int) called with value: " << a << " and name: " << name << std::endl;
    }

    // Overload for string argument
    void func(std::string str) {
        std::cout << "func4(string) called with value: " << str << " and name: " << name << std::endl;
    }

private:
    std::string name;
};

int main() {
    // default
    A* aPtr = new A();
    aPtr->setName("int Default Name");
    std::thread defThread(&A::funcDefault, aPtr, 100); // func4 expecting int
    defThread.join();
    delete aPtr;

    // Create an object of class A and call func4 with an int
    A* aPtr2 = new A();
    aPtr2->setName("int Name");
    std::thread iThread( (void(A::*)(int))&A::func, aPtr2, 100); // func4 expecting int

    iThread.join();
    delete aPtr2;

    // Create another object of class A and call func4 with a string
    A* sPtr = new A();
    sPtr->setName("string Name");
    std::thread sThread( static_cast<void(A::*)(std::string)>(&A::func), sPtr, "100 string"); // func4 expecting string
    sThread.join();
    delete sPtr;

    // Example of calling a free function (not a member function)
    auto free_func = [](int x) {
        std::cout << "free_func called with value: " << x << std::endl;
    };

    // Starting thread with a free function
    std::thread t_free(free_func, 150);
    t_free.join();

    return 0;
}

5.std::packaged_task 封装函数为可异步调用的对象

std::packaged_task 是 C++ 标准库中的一个类模板,用于将函数封装为一个可调用的对象,并可以异步执行该函数。它通常与 std::future 结合使用,用于获取函数执行结果。

代码:

#include <iostream>
#include <future>
#include <thread>
#include <functional>

// 任务函数
int taskFunction(int a, int b) {
    std::this_thread::sleep_for(std::chrono::seconds(2)); // 模拟耗时操作
    return a + b;
}

int main() {
    // 创建一个 packaged_task,绑定任务函数
    std::packaged_task<int(int, int)> task(taskFunction);

    // 获取与 packaged_task 关联的 future
    std::future<int> future = task.get_future();

    // 启动一个新线程执行任务,并传递参数
    std::thread t(std::move(task), 1, 2);

    // 等待任务执行完成并获取结果
    int result = future.get();

    // 输出结果
    std::cout << "Result: " << result << std::endl;

    // 等待线程结束
    t.join();

    // 重新设置 packaged_task 关联的任务函数
    std::packaged_task<int(int, int)> task2;
    task2.reset(); // 重置为默认构造函数创建的状态

    return 0;
}

6. placement new

“placement new” 是一种特殊形式的 new,它允许在指定的内存地址上构造对象,而不是在堆上分配新的内存。这种形式的 new 主要用于特殊的内存管理需求,例如在已经分配的内存块中创建对象,或在特定的内存区域(如共享内存或硬件寄存器)中构造对象。

为什么写这个呢?不怎么用的东西,然而可能在一些代码里看到,不懂这个就会懵掉。

a. placement new 的函数声明:

#include <new>  // 需要包含头文件

void* operator new(size_t size, void* ptr) noexcept;
void operator delete(void* ptr, void* place) noexcept;

b. 代码:

  • 解释:
  1. 使用 placement new 在指定的内存地址上构造对象。注意:不是去申请堆内存,而是用户指定的内存地址上构造类。
  2. 使用 operator delete(ptr, place) 释放对象,其中 ptr 是对象的指针,place 是对象所在的内存地址。
#include <iostream>
#include <new> // 包含 placement new

class MyClass {
public:
    MyClass(int value) : m_value(value) {
        std::cout << "Constructor called with value: " << m_value << std::endl;
    }

    ~MyClass() {
        std::cout << "Destructor called for value: " << m_value << std::endl;
    }

    void printValue() {
        std::cout << "Value: " << m_value << std::endl;
    }

private:
    int m_value;
};

int main() {
    char buffer[sizeof(MyClass)];  // 预分配一块内存,大小足够容纳 MyClass 对象

    MyClass* myObject = new (buffer) MyClass(42); // 在预分配的内存块中使用 placement new 构造 MyClass 对象

    myObject->printValue(); // 调用对象的成员函数

    myObject->~MyClass(); // 需要显式调用对象的析构函数(不会自动调用),注意不要使用 delete

    operator delete(myObject, buffer);  // 不能调用默认 delete,用户指定内存不是一定是堆内存程序可能会寄掉。另外 本行代码的  delete 一般也是不需要调用的,析构函数中处理好即可。

    return 0;
}

c. 场景

  1. 定位构造和销毁对象:当需要在指定的内存地址上构造和销毁对象时,可以使用 placement new operator delete(ptr, place)
  2. 定制内存管理:在一些特殊的内存管理场景中,可能需要精确地控制对象的内存分配和释放,这时可以使用该方法。
  3. 对象池:在实现对象池时,可以使用 placement newoperator delete(ptr, place) 来管理对象的分配和释放。
  4. 硬件寄存器映射:在嵌入式系统等场景中,可以使用该方法将对象映射到特定的硬件寄存器地址上。

[持续。。。]

最近更新:2024-05-09

the end~


- 推荐文章 -

C++

音视频


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值