C++中包装器的使用示例

 更新时间:2026年02月02日 09:17:03   作者:上去我就QWER  
C++中的包装器是一种结构型设计模式,用于包装已有对象以转换接口或增强功能,下面就来详细的介绍一下如何使用,具有一定的参考价值,感兴趣的可以了解一下

在 C++ 中,包装器(Wrapper) 也常被称为适配器(Adapter),是一种设计模式(结构型模式)的实现——核心作用是包装一个已有对象(或函数、指针等),转换其接口、隐藏实现细节,或增强其功能,让原本不兼容的接口能够协同工作,同时不改变原对象的逻辑。

包装器的核心价值:解耦、复用、接口统一。它就像一个“翻译官”,让两个原本无法直接沟通的组件(如不同接口的类、不同格式的函数)能够正常交互。

一、C++ 中常见的包装器类型

C++ 标准库提供了多种内置包装器,同时也支持自定义包装器。按包装对象的不同,主要分为以下几类:

1. 函数包装器(std::function)

最常用的包装器,用于包装任意可调用对象(函数指针、lambda 表达式、函数对象、类成员函数等),统一其调用接口。

核心用途:

  • 消除可调用对象的类型差异,实现“类型擦除”(例如,不同签名的函数统一用 std::function 存储);
  • 作为回调函数的统一载体(如 GUI 事件回调、异步任务回调)。

语法与示例:

#include <functional>
#include <iostream>

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

// 函数对象(仿函数)
struct Multiply {
    int operator()(int a, int b) { return a * b; }
};

// 类成员函数
class Calculator {
public:
    int subtract(int a, int b) { return a - b; }
};

int main() {
    // 包装普通函数
    std::function<int(int, int)> func1 = add;
    std::cout << func1(2, 3) << std::endl;  // 输出 5

    // 包装函数对象
    std::function<int(int, int)> func2 = Multiply{};
    std::cout << func2(2, 3) << std::endl;  // 输出 6

    // 包装 lambda 表达式
    std::function<int(int, int)> func3 = [](int a, int b) { return a / b; };
    std::cout << func3(6, 2) << std::endl;  // 输出 3

    // 包装类成员函数(需绑定实例)
    Calculator calc;
    std::function<int(int, int)> func4 = std::bind(&Calculator::subtract, &calc, std::placeholders::_1, std::placeholders::_2);
    std::cout << func4(5, 2) << std::endl;  // 输出 3

    return 0;
}

关键说明:

  • std::function 的模板参数是函数签名(返回值类型 + 参数类型列表);
  • 支持拷贝、赋值,可作为容器元素(如 std::vector<std::function<int(int)>>);
  • 空的 std::function 调用会抛出 std::bad_function_call 异常。

2. 指针包装器(智能指针:std::unique_ptr/std::shared_ptr)

智能指针是裸指针的包装器,核心作用是自动管理内存(避免内存泄漏),同时提供与裸指针兼容的接口(如 operator*operator->)。

核心用途:

  • 替代裸指针,实现“资源自动释放”(RAII 机制);
  • 隐藏内存管理细节(如引用计数、独占所有权)。

示例:

#include <memory>
#include <iostream>

class MyClass {
public:
    MyClass() { std::cout << "MyClass 构造" << std::endl; }
    ~MyClass() { std::cout << "MyClass 析构" << std::endl; }
    void show() { std::cout << "Hello MyClass" << std::endl; }
};

int main() {
    // std::unique_ptr:独占所有权
    std::unique_ptr<MyClass> ptr1 = std::make_unique<MyClass>();
    ptr1->show();  // 接口与裸指针一致

    // std::shared_ptr:共享所有权(引用计数)
    std::shared_ptr<MyClass> ptr2 = std::make_shared<MyClass>();
    std::shared_ptr<MyClass> ptr3 = ptr2;  // 引用计数+1
    ptr3->show();

    // 无需手动 delete,作用域结束时自动析构
    return 0;
}

关键说明:

  • 智能指针是“资源包装器”,遵循 RAII(资源获取即初始化)原则;
  • 包装了裸指针的“内存管理”逻辑,但对外提供与裸指针兼容的接口,不改变原类的使用方式。

3. 迭代器适配器(std::reverse_iterator/std::insert_iterator等)

迭代器适配器是原生迭代器的包装器,用于转换迭代器的行为(如反向遍历、插入元素而非覆盖),让同一容器支持不同的遍历/操作方式。

常见类型:

  • std::reverse_iterator:反向迭代器(包装正向迭代器,实现反向遍历);
  • std::insert_iterator:插入迭代器(包装容器迭代器,实现“插入”而非“覆盖”赋值);
  • std::back_insert_iterator/std::front_insert_iterator:尾部/头部插入迭代器。

示例(反向迭代器):

#include <vector>
#include <iostream>
#include <iterator>  // 包含迭代器适配器

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};

    // 正向遍历
    std::cout << "正向遍历:";
    for (auto it = vec.begin(); it != vec.end(); ++it) {
        std::cout << *it << " ";  // 输出 1 2 3 4 5
    }
    std::cout << std::endl;

    // 反向遍历(使用 reverse_iterator 包装 begin()/end())
    std::cout << "反向遍历:";
    for (auto it = std::reverse_iterator(vec.end()); it != std::reverse_iterator(vec.begin()); ++it) {
        std::cout << *it << " ";  // 输出 5 4 3 2 1
    }
    std::cout << std::endl;

    return 0;
}

关键说明:

  • 迭代器适配器不改变容器的存储结构,仅包装迭代器的 operator++/operator* 等行为;
  • 对外提供统一的迭代器接口(如 ++*),用户无需关心底层实现差异。

4. 函数适配器(std::bind/std::not_fn)

函数适配器用于包装可调用对象,修改其参数列表或逻辑(如绑定参数、取反逻辑),生成新的可调用对象。

(1)std::bind:参数绑定适配器

核心作用:绑定可调用对象的部分参数,生成参数更少的新函数。

示例:

#include <functional>
#include <iostream>

int add(int a, int b, int c) { return a + b + c; }

int main() {
    // 绑定第一个参数为 10,生成新函数:int(int, int)
    auto add10 = std::bind(add, 10, std::placeholders::_1, std::placeholders::_2);
    std::cout << add10(2, 3) << std::endl;  // 10+2+3=15

    // 绑定后两个参数为 5、6,生成新函数:int(int)
    auto add56 = std::bind(add, std::placeholders::_1, 5, 6);
    std::cout << add56(4) << std::endl;  // 4+5+6=15

    return 0;
}

(2)std::not_fn:逻辑取反适配器

核心作用:包装返回布尔值的可调用对象,生成“逻辑取反”的新函数(C++17 引入)。

示例:

#include <functional>
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};

    // 查找偶数(原逻辑:x % 2 == 0)
    auto even = [](int x) { return x % 2 == 0; };
    auto it1 = std::find_if(vec.begin(), vec.end(), even);
    std::cout << "第一个偶数:" << *it1 << std::endl;  // 输出 2

    // 查找奇数(包装 even,逻辑取反)
    auto odd = std::not_fn(even);
    auto it2 = std::find_if(vec.begin(), vec.end(), odd);
    std::cout << "第一个奇数:" << *it2 << std::endl;  // 输出 1

    return 0;
}

5. 自定义包装器(类适配器)

除了标准库提供的包装器,也可以手动实现类级别的包装器,用于适配不同接口的类。

场景:

假设已有一个旧接口的类 OldInterface,新代码需要使用统一的 NewInterface 接口,此时可通过包装器适配。

示例:

#include <iostream>
#include <string>

// 旧接口(无法修改)
class OldInterface {
public:
    void do_old_task(const std::string& msg) {
        std::cout << "旧接口执行任务:" << msg << std::endl;
    }
};

// 新接口(统一规范)
class NewInterface {
public:
    virtual ~NewInterface() = default;
    virtual void execute(const std::string& task) = 0;
};

// 包装器(适配器):将 OldInterface 适配为 NewInterface
class OldToNewAdapter : public NewInterface {
private:
    OldInterface old_obj;  // 包装旧接口对象
public:
    void execute(const std::string& task) override {
        // 转换接口:新接口的 execute 调用旧接口的 do_old_task
        old_obj.do_old_task("适配后:" + task);
    }
};

// 新代码使用统一接口
void use_new_interface(NewInterface& obj) {
    obj.execute("完成数据处理");
}

int main() {
    OldToNewAdapter adapter;
    use_new_interface(adapter);  // 输出:旧接口执行任务:适配后:完成数据处理
    return 0;
}

关键说明:

  • 自定义包装器通常采用“组合”(包装被适配对象)或“继承”(适配接口)的方式;
  • 核心是“接口转换”:对外提供目标接口,对内调用被包装对象的原有接口。

二、包装器的核心特性

  1. 接口转换:将被包装对象的接口转换为目标接口(如 OldInterfaceNewInterface);
  2. 透明性:用户无需关心被包装对象的细节,仅需使用包装器提供的统一接口;
  3. 增强功能:可在包装器中添加额外逻辑(如日志、缓存、权限校验),不修改原对象;
  4. 复用性:同一包装器可适配多个同类被包装对象(如 std::function 可包装任意符合签名的可调用对象)。

三、包装器与装饰器的区别(易混淆点)

很多人会将包装器(适配器)与装饰器(Decorator)混淆,两者核心差异在于目的不同

  • 「包装器(适配器)」:核心是接口适配(让不兼容的接口协同工作);
  • 「装饰器」:核心是功能增强(在不改变接口的前提下,动态添加新功能)。

示例区别:

  • 适配器:将 OldInterface::do_old_task 适配为 NewInterface::execute(接口变了);
  • 装饰器:给 NewInterface::execute 增加日志功能(接口不变,功能变了)。

总结

C++ 中的包装器(适配器)是“接口转换与复用”的核心工具,分为:

  • 标准库内置包装器:std::function(函数)、智能指针(指针)、迭代器适配器(迭代器)、std::bind(函数参数);
  • 自定义包装器:类级别的接口适配。

其核心价值是解耦(隔离不同接口的组件)、复用(无需修改原有代码)、统一接口(降低使用成本),是 C++ 中实现灵活设计的重要手段。

到此这篇关于C++中包装器的使用示例的文章就介绍到这了,更多相关C++ 包装器内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C++中list实现双向循环链表详细解析

    C++中list实现双向循环链表详细解析

    双向循环链表是一种重要的线性数据结构,支持前后双向遍历,适用于频繁插入删除和高效访问的场景,这篇文章主要介绍了C++中list实现双向循环链表详细解析的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2026-01-01
  • c++实现通用参数解析类示例

    c++实现通用参数解析类示例

    使用命令行执行程序的时候在程序后可跟多个参数列表,而main函数的argc和argv分别存储了相关的参数个数和参数内容,而循环输入相关的时候就需要用户自己来解析相关参数。以下代码用c++的方式实现了相关解析的封装,使用起来非常方便
    2014-03-03
  • C++记录程序运行时间的四种方法

    C++记录程序运行时间的四种方法

    在学习过程中很重要的一个必会的小技巧:计算某一段代码的执行时间,可以用来分析代码的效率和算法的时间复杂度等等(个人主要是在总结各种排序算法时遇到的这个方法),本文给大家介绍了C++记录程序运行时间的四种方法,需要的朋友可以参考下
    2025-03-03
  • 用c++实现x的y次幂的代码

    用c++实现x的y次幂的代码

    以下实例是对使用c++实现x的y次幂的解决方法进行了介绍。需要的朋友参考下
    2013-05-05
  • C语言实现词法分析器

    C语言实现词法分析器

    这篇文章主要为大家详细介绍了C语言实现词法分析器,一个简单的词法分析程序,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-01-01
  • C++之内存分区的实现示例

    C++之内存分区的实现示例

    本文主要介绍了C++之内存分区的实现示例,主要包含了4个区域,分为代码区,全局区,栈区和堆区,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧
    2024-03-03
  • FFRPC应用 Client/Server使用及原理解析

    FFRPC应用 Client/Server使用及原理解析

    这篇文章主要介绍了FFRPC应用 Client/Server使用及原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-08-08
  • 详解C++编程中的嵌套类的声明与其中的函数使用

    详解C++编程中的嵌套类的声明与其中的函数使用

    这篇文章主要介绍了C++编程中的嵌套类的声明与其中的函数使用,嵌套类即在一个类的范围内声明和编写另一个类,需要的朋友可以参考下
    2016-01-01
  • 文件编译时出现multiple definition of ''xxxxxx''的具体解决方法

    文件编译时出现multiple definition of ''xxxxxx''的具体解决方法

    以下是对文件编译时出现multiple definition of 'xxxxxx'的解决方法进行了详细的分析介绍,如也遇到此问题的朋友们可以过来参考下
    2013-07-07
  • C++将二叉树转为双向链表及判断两个链表是否相交

    C++将二叉树转为双向链表及判断两个链表是否相交

    这篇文章主要介绍了C++将二叉树转为双向链表及判断两个链表是否相交的方法,文中还给出了求两个链表相交的第一个节点列的实现方法,需要的朋友可以参考下
    2016-02-02

最新评论