C++函数指针和std::function对象

本文涉及的产品
实时计算 Flink 版,5000CU*H 3个月
实时数仓Hologres,5000CU*H 100GB 3个月
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
简介: 这篇博文中通过实现对String字符串大小写转换为列来说明C++中函数指针和std::function对象的使用。我们在博文《C++实现一个简单的String类》中的自定义的String类为基础,再添加两个成员函数用于将字符串全部转为大写(toUpperCase)和全部转为小写(toLowerCase)。

C++函数指针和std::function对象

这篇博文中通过实现对String字符串大小写转换为列来说明C++中函数指针和std::function对象的使用。

我们在博文《C++实现一个简单的String类》中的自定义的String类为基础,再添加两个成员函数用于将字符串全部转为大写(toUpperCase)和全部转为小写(toLowerCase)。

分析一下这两个函数,我们可以发现,两个函数的实现有相同之处,都需要变量字符串中的每个字符,然后使用大写转换函数(std::touuper)和小写转换函数(std::tolower)进行转换即可。

既然两个函数有相同的部分,我们可以将相同的部分抽取出来,抽取出来的这部分负责对字符串进行遍历,然后将对于单个字符转换的函数作为参数传递到该用于字符串遍历的函数中。

下面我们分别使用函数指针的方式和C++ 11中的std::function对象进行实现。本文不对std::function的优点进行介绍,这是以一个简单示例进行入门介绍。

函数指针

头文件:

头文件实现中我们使用了typedef定义了一个函数指针类型,当然我们也可以使用using关键字进行定义,两者类似。

String::map函数用于对字符串进行遍历操作,然后通过传进来的函数指针对每个字符进行操作。

注意我们定义的transform函数指针的返回值是int,函数参数也是int,这是因为cctype头文件中的std::toupperstd::tolower函数的签名也是这样的。

class String {
private:
    char* _buffer;
    size_t _length;
    // 使用using和typedef都可以: typedef int (*transform)(int);
    using transform = int (*)(int);
    void init(const char* str);
    String map(transform fun);

public:
    String(const char* str= nullptr);       // 默认构造函数
    String(const String& other);            // 拷贝构造函数
    String(String&& other) noexcept;        // 移动构造函数
    ~String();                              // 析构函数

    size_t length();
    const char* data();

    char& operator[](size_t index);
    String& operator=(const String& other);
    String& operator=(String&& other) noexcept;
    String operator+(const String& other);
    bool operator==(const String& other);

    String toUpperCase();
    String toLowerCase();

    friend std::ostream& operator<<(std::ostream& output, const String& str);
    friend std::istream& operator>>(std::istream& input, String& str);
};

具体实现:

String String::map(String::transform fun) {
    char* transformed = new char[_length];
    for (size_t i = 0; i < _length; ++i) {
        transformed[i] = static_cast<char>(fun(static_cast<int>(_buffer[i])));
    }
    return String(transformed);
}

String String::toUpperCase() {
    return map(std::toupper);
}

String String::toLowerCase() {
    return map(std::tolower);
}

测试文件:

#include "strings.h"
#include <iostream>

using std::cout;

int main() {
    String str1("Hello World");
    String str2 = str1.toUpperCase();
    cout << str2 << '\n';
    return 0;
}

输出结果:

默认构造函数(Hello World)
默认构造函数(HELLO WORLD)
HELLO WORLD
析构函数(HELLO WORLD)
析构函数(Hello World)

std::function对象

头文件

可以看到我们这里使用了std::function类型作为String::map函数的参数类型,std::function是一个模板类,尖括号中标识了返回值,圆括号中标识了参数列表(可以是多个)。

这里我们的std::function对象类型的返回值和参数列表都是char

(为什么不跟前面一样都用int呢?不感兴趣的可以忽略这一段。我做了测试:如果用int的话,会跟locale中定义的touppertolower函数定义冲突。locale头文件中的这两个函数的返回值和参数是char_type类型,编译不通过。所以我将std::function对象类型的返回值和参数列表定义为char,然后在String::toUpperCaseString::toLowerCase函数中使用匿名函数(Lambda)将cctype中的std::toupperstd::tolower函数的返回值和参数类型由int强制转换为char即可。)

class String {
private:
    char* _buffer;
    size_t _length;
    void init(const char* str);
    String map(std::function<char(char)> fun);

public:
    String(const char* str= nullptr);       // 默认构造函数
    String(const String& other);            // 拷贝构造函数
    String(String&& other) noexcept;        // 移动构造函数
    ~String();                              // 析构函数

    size_t length();
    const char* data();

    char& operator[](size_t index);
    String& operator=(const String& other);
    String& operator=(String&& other) noexcept;
    String operator+(const String& other);
    bool operator==(const String& other);

    String toUpperCase();
    String toLowerCase();

    friend std::ostream& operator<<(std::ostream& output, const String& str);
    friend std::istream& operator>>(std::istream& input, String& str);
};

实现代码:

在在String::toUpperCaseString::toLowerCase函数中使用可匿名函数(Lambda)对std::toupperstd::tolower函数的返回值和参数类型int进行了强制转换,这样才可以跟定义的std::function类型的函数签名相符。

String String::map(function<char(char)> fun) {
    char* transformed = new char[_length];
    for (size_t i = 0; i < _length; ++i) {
        transformed[i] = (fun(_buffer[i]));
    }
    return String(transformed);
}

String String::toUpperCase() {
    return map([](char c){return static_cast<char>(toupper(static_cast<int>(c)));});
}

String String::toLowerCase() {
    return map([](char c){return static_cast<char>(toupper(static_cast<int>(c)));});
}

主函数跟上面函数指针的一样,输出结果也完全一样。

这个案例虽然不能体现出使用std::function类型的优势,但是对于它的简单使用可以有一个参考。

目录
相关文章
|
24天前
|
存储 对象存储 C++
C++ 中 std::array<int, array_size> 与 std::vector<int> 的深入对比
本文深入对比了 C++ 标准库中的 `std::array` 和 `std::vector`,从内存管理、性能、功能特性、使用场景等方面详细分析了两者的差异。`std::array` 适合固定大小的数据和高性能需求,而 `std::vector` 则提供了动态调整大小的灵活性,适用于数据量不确定或需要频繁操作的场景。选择合适的容器可以提高代码的效率和可靠性。
52 0
|
24天前
|
存储 程序员 C++
深入解析C++中的函数指针与`typedef`的妙用
本文深入解析了C++中的函数指针及其与`typedef`的结合使用。通过图示和代码示例,详细介绍了函数指针的基本概念、声明和使用方法,并展示了如何利用`typedef`简化复杂的函数指针声明,提升代码的可读性和可维护性。
65 0
|
2月前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
172 4
|
3月前
|
存储 安全 编译器
在 C++中,引用和指针的区别
在C++中,引用和指针都是用于间接访问对象的工具,但它们有显著区别。引用是对象的别名,必须在定义时初始化且不可重新绑定;指针是一个变量,可以指向不同对象,也可为空。引用更安全,指针更灵活。
|
3月前
|
存储 C++
c++的指针完整教程
本文提供了一个全面的C++指针教程,包括指针的声明与初始化、访问指针指向的值、指针运算、指针与函数的关系、动态内存分配,以及不同类型指针(如一级指针、二级指针、整型指针、字符指针、数组指针、函数指针、成员指针、void指针)的介绍,还提到了不同位数机器上指针大小的差异。
84 1
|
3月前
|
存储 编译器 C语言
C++入门2——类与对象1(类的定义和this指针)
C++入门2——类与对象1(类的定义和this指针)
58 2
|
3月前
|
存储 安全 编译器
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值(一)
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值
|
3月前
对象指针输出时分秒
对象指针输出时分秒
14 0
|
3月前
|
存储 编译器 程序员
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值(二)
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值
|
2天前
|
C++ 芯片
【C++面向对象——类与对象】Computer类(头歌实践教学平台习题)【合集】
声明一个简单的Computer类,含有数据成员芯片(cpu)、内存(ram)、光驱(cdrom)等等,以及两个公有成员函数run、stop。只能在类的内部访问。这是一种数据隐藏的机制,用于保护类的数据不被外部随意修改。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。成员可以在派生类(继承该类的子类)中访问。成员,在类的外部不能直接访问。可以在类的外部直接访问。为了完成本关任务,你需要掌握。
33 18