简介

  • C/C++常用的编程技巧

C++ 将uchar数组转为字符串

要将 std::vector<uchar>(或者 uchar[] 数组)转换为字符串,您可以使用字符串流 std::stringstream 或者直接操作字符数组的方法。下面是这两种方法的示例代码:

使用字符串流 std::stringstream

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <vector>
#include <sstream>

int main() {
    std::vector<unsigned char> buffer = {0x48, 0x65, 0x6C, 0x6C, 0x6F}; // 示例uchar数组

    // 使用字符串流
    std::stringstream ss;
    for (unsigned char byte : buffer) {
        ss << byte;
    }

    std::string result = ss.str();

    std::cout << "Result using stringstream: " << result << std::endl;

    return 0;
}

直接操作字符数组的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
#include <vector>

int main() {
    std::vector<unsigned char> buffer = {0x48, 0x65, 0x6C, 0x6C, 0x6F}; // 示例uchar数组

    // 直接操作字符数组
    std::string result(buffer.begin(), buffer.end());

    std::cout << "Result using direct manipulation: " << result << std::endl;

    return 0;
}

这两种方法都会将 buffer 中的数据转换为字符串 result。您可以根据您的偏好选择其中之一来实现。

C++ 父类调用子类的成员函数

在 C++ 中,父类无法直接调用子类的成员函数。通常情况下,父类对子类的行为是无知的,这是遵循了面向对象编程的封装原则。

但是,如果你有一个父类指针或引用指向一个子类对象,并且子类对象中有重写(override)了父类的成员函数,那么通过这个指针或引用调用的成员函数将会是子类中的版本,这被称为多态性。这是 C++ 中实现运行时多态的一种方式。

以下是一个简单的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>

class Base {
public:
    virtual void foo() {
        std::cout << "Base::foo()" << std::endl;
    }
};

class Derived : public Base {
public:
    void foo() override {
        std::cout << "Derived::foo()" << std::endl;
    }
};

int main() {
    Base* basePtr = new Derived(); // 父类指针指向子类对象
    basePtr->foo(); // 调用子类的成员函数
    delete basePtr;
    return 0;
}

在这个示例中,Base 类有一个虚拟函数 foo()Derived 类重写了这个函数。在 main() 函数中,basePtr 是一个指向 Base 类的指针,但它指向一个 Derived 类的对象。当调用 basePtr->foo() 时,实际上调用的是 Derived 类中的 foo() 函数,因为它已经被重写了。

C++ 子类对象调用父类函数

在C++中,子类对象可以调用父类函数。这是通过继承来实现的。假设有一个父类Parent和一个子类ChildChildParent的派生类。如果Child想要调用Parent的函数,可以使用以下方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>

class Parent {
public:
    void parentFunction() {
        std::cout << "This is the parent function." << std::endl;
    }
};

class Child : public Parent {
public:
    void childFunction() {
        std::cout << "This is the child function." << std::endl;
    }

    void callParentFunction() {
        parentFunction(); // 调用父类函数
    }
};

int main() {
    Child childObj;
    childObj.callParentFunction(); // 调用子类对象的函数,间接调用父类函数

    return 0;
}

在这个例子中,Child类继承了Parent类。Child类中的callParentFunction函数通过parentFunction()直接调用了父类Parent的函数。在main()函数中,创建了Child类的对象childObj,然后通过childObj.callParentFunction()来调用子类的函数,间接地调用了父类的函数。

C++ linux 获取硬盘序列号

在Linux系统上,获取硬盘序列号通常需要依赖于特定的命令行工具或文件。一个常用的方法是使用udevadm工具来获取硬盘的序列号。下面是一个示例代码,演示如何使用C++调用udevadm命令获取硬盘序列号:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <iostream>
#include <cstdio>
#include <memory>
#include <stdexcept>
#include <array>
#include <string>
#include <regex>

// Execute a shell command and return the output as a string
std::string exec(const char* cmd) {
    std::array<char, 128> buffer;
    std::string result;
    std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);
    if (!pipe) {
        throw std::runtime_error("popen() failed!");
    }
    while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
        result += buffer.data();
    }
    return result;
}

// Get disk serial number by executing udevadm command
std::string GetDiskSerialNumber() {
    try {
        std::string output = exec("udevadm info --query=all --name=/dev/sda | grep ID_SERIAL_SHORT");
        std::regex regex("\\bID_SERIAL_SHORT=(.*)\\b");
        std::smatch match;
        if (std::regex_search(output, match, regex) && match.size() > 1) {
            return match.str(1);
        } else {
            return "Unknown";
        }
    } catch (const std::exception& e) {
        return "Error";
    }
}

int main() {
    std::string serialNumber = GetDiskSerialNumber();
    std::cout << "Disk Serial Number: " << serialNumber << std::endl;
    return 0;
}

在这个示例中,exec函数用于执行shell命令并返回输出。GetDiskSerialNumber函数调用udevadm命令来获取硬盘的序列号,然后使用正则表达式来提取序列号信息。请确保你的系统上已经安装了udevadm命令,否则这段代码将无法工作。

值得注意的是,硬盘序列号的获取方法可能因不同的Linux发行版或系统配置而有所不同。因此,你可能需要根据具体情况进行调整。

linux CPU ID查询

在Linux系统上,要获取CPU ID通常需要从CPU信息文件中读取信息。在大多数系统中,这些信息位于/proc/cpuinfo文件中。CPU ID在这个文件中通常以cpu_idprocessor字段的形式出现。

下面是一个示例代码,演示如何使用C++读取/proc/cpuinfo文件来获取CPU ID:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <iostream>
#include <fstream>
#include <string>
#include <regex>
#include <unordered_set>

// Function to read CPU IDs from /proc/cpuinfo
std::unordered_set<std::string> GetCPUIDs() {
    std::unordered_set<std::string> cpuIDs;
    std::ifstream cpuinfoFile("/proc/cpuinfo");
    if (cpuinfoFile.is_open()) {
        std::string line;
        std::regex idRegex("^processor\\s*:\\s*(\\d+)");
        while (std::getline(cpuinfoFile, line)) {
            std::smatch match;
            if (std::regex_search(line, match, idRegex) && match.size() > 1) {
                cpuIDs.insert(match.str(1));
            }
        }
        cpuinfoFile.close();
    } else {
        std::cerr << "Failed to open /proc/cpuinfo" << std::endl;
    }
    return cpuIDs;
}

int main() {
    std::unordered_set<std::string> cpuIDs = GetCPUIDs();
    if (!cpuIDs.empty()) {
        std::cout << "CPU IDs:" << std::endl;
        for (const std::string& id : cpuIDs) {
            std::cout << id << std::endl;
        }
    } else {
        std::cerr << "No CPU IDs found." << std::endl;
    }
    return 0;
}

这个示例代码打开/proc/cpuinfo文件并按行读取其中的内容。使用正则表达式匹配每一行,找到processor字段,然后提取其值作为CPU ID。CPU ID通常是一个数字,它表示CPU的逻辑核心。因此,这个示例代码将CPU ID存储在一个无序集合中,以确保每个CPU ID只出现一次。

请注意,这个示例代码假设/proc/cpuinfo文件的格式在不同的Linux系统中是相同的。如果你的系统上的文件格式与预期不同,你可能需要根据实际情况调整正则表达式以确保正确提取CPU ID。

linux 主板序列号查询

在Linux系统上,获取主板序列号的方法因系统和硬件而异。一种常见的方法是通过dmidecode命令来获取系统信息,包括主板信息。dmidecode命令可以解析DMI(Desktop Management Interface)信息,其中包含了主板的序列号等信息。

以下是一个示例代码,演示如何使用C++调用dmidecode命令来获取主板序列号:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <iostream>
#include <cstdio>
#include <memory>
#include <stdexcept>
#include <array>
#include <string>
#include <regex>

// Execute a shell command and return the output as a string
std::string exec(const char* cmd) {
    std::array<char, 128> buffer;
    std::string result;
    std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);
    if (!pipe) {
        throw std::runtime_error("popen() failed!");
    }
    while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
        result += buffer.data();
    }
    return result;
}

// Get motherboard serial number by executing dmidecode command
std::string GetMotherboardSerialNumber() {
    try {
        std::string output = exec("dmidecode -t baseboard | grep 'Serial Number'");
        std::regex regex("\\bSerial Number:\\s*(.*)\\b");
        std::smatch match;
        if (std::regex_search(output, match, regex) && match.size() > 1) {
            return match.str(1);
        } else {
            return "Unknown";
        }
    } catch (const std::exception& e) {
        return "Error";
    }
}

int main() {
    std::string serialNumber = GetMotherboardSerialNumber();
    std::cout << "Motherboard Serial Number: " << serialNumber << std::endl;
    return 0;
}

在这个示例中,exec函数用于执行shell命令并返回输出。GetMotherboardSerialNumber函数调用dmidecode命令来获取主板的序列号,并使用正则表达式来提取序列号信息。请确保你的系统上已经安装了dmidecode命令,否则这段代码将无法工作。

需要注意的是,主板序列号的获取方法可能因不同的Linux发行版或系统配置而有所不同。因此,你可能需要根据具体情况进行调整。

linux 操作系统生成的UUID

Linux操作系统通常会为每个安装的系统生成一个唯一的UUID(通用唯一标识符)。这个UUID通常存储在/etc/machine-id文件中。你可以通过读取这个文件来获取操作系统生成的UUID。

以下是一个示例代码,演示如何使用C++读取/etc/machine-id文件来获取操作系统生成的UUID:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>
#include <fstream>
#include <string>

// Function to read UUID from /etc/machine-id
std::string GetOSUUID() {
    std::ifstream machineIdFile("/etc/machine-id");
    if (!machineIdFile.is_open()) {
        std::cerr << "Failed to open /etc/machine-id" << std::endl;
        return "Error";
    }

    std::string uuid;
    std::getline(machineIdFile, uuid);
    machineIdFile.close();

    return uuid;
}

int main() {
    std::string osUUID = GetOSUUID();
    if (osUUID != "Error") {
        std::cout << "Operating System UUID: " << osUUID << std::endl;
    } else {
        std::cerr << "Failed to get Operating System UUID." << std::endl;
    }
    return 0;
}

在这个示例中,GetOSUUID函数用于读取/etc/machine-id文件并返回其中的UUID。如果文件不存在或读取失败,函数将返回字符串”Error”。

请注意,UUID的格式通常是一串32位的十六进制字符,可能带有连字符(破折号)。

C++ 获取机器标识的方法

获取机器标识通常意味着获取一些唯一的硬件信息,以便能够在不同的机器之间进行区分。以下是一些常见的获取机器标识的方法:

  1. MAC 地址:每个网络适配器(网卡)都有一个唯一的 MAC 地址,可以用来区分不同的机器。

  2. 硬盘序列号:硬盘通常都有一个唯一的序列号。

  3. CPU ID:处理器也有一个唯一的标识符。

  4. 主板序列号:主板上可能有一个唯一的序列号或者标识符。

  5. 操作系统生成的 UUID:一些操作系统会为每台机器生成一个唯一的 UUID。

下面是一个示例代码,演示如何获取 MAC 地址作为机器标识:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>
#include <fstream>
#include <sstream>
#include <stdexcept>
#include <vector>
#include <algorithm>
#include <iomanip>

std::string GetMACAddress() {
    std::ifstream file("/sys/class/net/eth0/address");
    if (!file.is_open()) {
        throw std::runtime_error("Failed to open MAC address file");
    }

    std::stringstream buffer;
    buffer << file.rdbuf();
    std::string macAddress = buffer.str();

    // Remove newline characters if present
    macAddress.erase(std::remove(macAddress.begin(), macAddress.end(), '\n'), macAddress.end());

    return macAddress;
}

int main() {
    try {
        std::string macAddress = GetMACAddress();
        std::cout << "MAC Address: " << macAddress << std::endl;

        // You can use this MAC address as the machine identifier
        // Be sure to handle exceptions if the MAC address retrieval fails
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
    }

    return 0;
}

这个示例代码假设了网卡的设备名称为eth0,并尝试从 /sys/class/net/eth0/address 文件中读取 MAC 地址。你可以根据需要修改设备名称或者尝试其他途径获取 MAC 地址,比如通过系统调用来获取网络接口信息。

C++ 生成唯一且固定的标识

要在C++中生成唯一且固定的标识,你可以结合多种因素来生成唯一性,比如系统时间、硬件信息、随机数等。以下是一个简单的示例,结合了系统时间和随机数生成唯一的标识:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <iostream>
#include <sstream>
#include <chrono>
#include <random>

// 生成唯一标识符
std::string GenerateUniqueID() {
    // 获取当前时间戳
    auto now = std::chrono::system_clock::now();
    auto duration = now.time_since_epoch();
    auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();

    // 生成随机数
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> dis(0, 9999);
    int randomNum = dis(gen);

    // 构建唯一标识符
    std::ostringstream oss;
    oss << millis << "-" << randomNum;
    return oss.str();
}

int main() {
    std::string uniqueID = GenerateUniqueID();
    std::cout << "Unique ID: " << uniqueID << std::endl;

    return 0;
}

在这个示例中,我们使用了当前系统时间的毫秒级别作为唯一标识的一部分,并结合了一个随机数。这样可以在很大程度上保证生成的标识是唯一且固定的。你也可以根据需要添加其他因素来增强唯一性,比如硬件信息、进程ID等。

获取mac地址

Boost 库本身并没有直接提供获取 MAC 地址的功能,但你可以使用操作系统提供的接口来获取。在 Linux 上,你可以通过读取 /sys/class/net/<interface>/address 文件来获取 MAC 地址,其中 <interface> 是你要获取 MAC 地址的网络接口名称。

以下是一个使用 Boost 库的示例代码,结合读取 MAC 地址的功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <iostream>
#include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp>

namespace fs = boost::filesystem;

std::string GetMacAddress(const std::string& interfaceName) {
    fs::path addressFile("/sys/class/net/" + interfaceName + "/address");
    if (!fs::exists(addressFile)) {
        std::cerr << "Error: Interface " << interfaceName << " does not exist." << std::endl;
        return "";
    }

    std::ifstream file(addressFile.c_str());
    if (!file.is_open()) {
        std::cerr << "Error: Failed to open address file for interface " << interfaceName << std::endl;
        return "";
    }

    std::string macAddress;
    std::getline(file, macAddress);
    file.close();

    // Trim newline characters
    boost::algorithm::trim(macAddress);
    return macAddress;
}

int main() {
    std::string interfaceName = "eth0"; // 修改为你要获取 MAC 地址的网络接口名称
    std::string macAddress = GetMacAddress(interfaceName);
    if (!macAddress.empty()) {
        std::cout << "MAC Address for interface " << interfaceName << ": " << macAddress << std::endl;
    } else {
        std::cerr << "Failed to get MAC Address for interface " << interfaceName << std::endl;
    }

    return 0;
}

在这个示例中,我们使用 Boost 库来处理字符串,并使用 Boost.Filesystem 库来检查文件是否存在。然后,我们打开地址文件并读取其中的 MAC 地址。

需要注意的是,你需要确保在你的系统上安装了 Boost 库,并且添加了正确的编译链接选项。

C++ 遍历enum class数据类型的对象

在C++中,要遍历enum class数据类型的对象,你可以使用基于范围的for循环(range-based for loop)或者迭代器。这里我提供两种方法的示例代码:

方法一:基于范围的for循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>

enum class Color {
    Red,
    Green,
    Blue
};

int main() {
    for (int i = static_cast<int>(Color::Red); i <= static_cast<int>(Color::Blue); ++i) {
        Color currentColor = static_cast<Color>(i);

        switch (currentColor) {
            case Color::Red:
                std::cout << "Red\n";
                break;
            case Color::Green:
                std::cout << "Green\n";
                break;
            case Color::Blue:
                std::cout << "Blue\n";
                break;
        }
    }

    return 0;
}

方法二:使用迭代器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>

enum class Color {
    Red,
    Green,
    Blue
};

int main() {
    for (Color currentColor = Color::Red; currentColor <= Color::Blue; static_cast<int>(currentColor)++) {
        switch (currentColor) {
            case Color::Red:
                std::cout << "Red\n";
                break;
            case Color::Green:
                std::cout << "Green\n";
                break;
            case Color::Blue:
                std::cout << "Blue\n";
                break;
        }
    }

    return 0;
}

请注意,这两种方法都需要你知道enum class的底层类型,并使用static_cast将其转换为整数。在这两个示例中,底层类型是int。确保你的enum class中的值是按照顺序排列的,这样遍历才能按照期望进行。

C++ 判断一个点xy是否在四个点围成的四边形中

在C++中,可以使用以下方法判断一个点 (x, y) 是否在由四个点围成的四边形内。假设四个点的坐标分别为 (x1, y1), (x2, y2), (x3, y3), (x4, y4)

首先,你可以使用射线法(Ray Casting)或者叉积法(Cross Product)来进行判断。以下是使用叉积法的示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <iostream>

using namespace std;

// 计算两个向量的叉积
int crossProduct(int x1, int y1, int x2, int y2, int x3, int y3) {
    return (x2 - x1) * (y3 - y1) - (y2 - y1) * (x3 - x1);
}

// 判断点是否在四边形内
bool isPointInsideQuadrilateral(int x, int y, int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) {
    bool isInside = false;

    int cross1 = crossProduct(x1, y1, x2, y2, x, y);
    int cross2 = crossProduct(x2, y2, x3, y3, x, y);
    int cross3 = crossProduct(x3, y3, x4, y4, x, y);
    int cross4 = crossProduct(x4, y4, x1, y1, x, y);

    // 如果四个叉积的符号都相同,则点在四边形内
    if ((cross1 >= 0 && cross2 >= 0 && cross3 >= 0 && cross4 >= 0) ||
        (cross1 <= 0 && cross2 <= 0 && cross3 <= 0 && cross4 <= 0)) {
        isInside = true;
    }

    return isInside;
}

int main() {
    // 示例坐标
    int x1 = 0, y1 = 0;
    int x2 = 2, y2 = 0;
    int x3 = 2, y3 = 2;
    int x4 = 0, y4 = 2;

    // 待判断的点坐标
    int x = 1, y = 1;

    // 判断点是否在四边形内
    if (isPointInsideQuadrilateral(x, y, x1, y1, x2, y2, x3, y3, x4, y4)) {
        cout << "点在四边形内" << endl;
    } else {
        cout << "点不在四边形内" << endl;
    }

    return 0;
}

这个程序中,crossProduct 函数计算了两个向量的叉积,而 isPointInsideQuadrilateral 函数则使用了这个叉积来判断点是否在四边形内。程序中给出的示例是一个单位正方形,你可以根据实际需要修改坐标和点的位置。

C++ 创建包含布尔值的数组

在C++中,你可以使用标准库的std::vector来创建包含布尔值的数组。以下是一个简单的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <vector>

int main() {
    // 创建包含布尔值的数组
    std::vector<bool> boolArray = {true, false, true, true, false};

    // 遍历数组并打印每个元素
    for (bool value : boolArray) {
        std::cout << std::boolalpha << value << " ";
    }

    return 0;
}

在上述示例中,我们使用std::vector<bool>来创建一个包含布尔值的数组 boolArray。在遍历数组时,我们使用 std::boolalpha 将布尔值打印为 truefalse

请注意,std::vector<bool> 在C++中的实现可能不同于其他类型的std::vector,因为它经过了优化以节省空间。如果你需要更一般的std::vector,可以使用std::vector<bool>的替代品,例如使用std::vector<char>std::vector<int>

C++ std::vector 通过迭代器获取当前元素的索引

在C++中,std::vector的迭代器本身并不提供直接的索引访问功能。但你可以使用一些其他方法来获取当前迭代器指向元素的索引。

以下是一种使用std::distance函数的方法,该函数返回两个迭代器之间的距离:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <vector>

int main() {
    std::vector<int> myVector = {10, 20, 30, 40, 50};

    for (auto it = myVector.begin(); it != myVector.end(); ++it) {
        // 使用 std::distance 获取迭代器之间的距离,即当前元素的索引
        int index = std::distance(myVector.begin(), it);
        std::cout << "Index: " << index << ", Value: " << *it << std::endl;
    }

    return 0;
}

这段代码使用了std::distance函数,该函数接受两个迭代器参数,并返回它们之间的距离(即元素的索引)。在上面的例子中,std::distance(myVector.begin(), it)返回迭代器itmyVector.begin()之间的距离,即当前元素的索引。

请注意,这种方法在一些情况下可能不是最有效的,因为std::distance的时间复杂度为O(N),其中N是迭代器之间的距离。如果你需要频繁地获取索引并且性能很重要,可能需要考虑其他数据结构或方法。

C++ for范围循环 怎么判断当前元素是否是最后一个

在C++中,使用范围循环(range-based for loop)时,判断当前元素是否是最后一个元素并不是直接支持的特性。范围循环是为了简化迭代过程,因此在标准语法中并没有提供直接的方式来检测是否是最后一个元素。

通常,可以通过以下几种方式来处理这个问题:

1. 使用迭代器

使用迭代器,手动追踪当前位置,并判断是否是尾后迭代器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
#include <vector>

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

    for (auto it = numbers.begin(); it != numbers.end(); ++it) {
        // 判断是否是最后一个元素
        if (it == std::prev(numbers.end())) {
            std::cout << "Last element: " << *it << std::endl;
        } else {
            std::cout << *it << ", ";
        }
    }

    return 0;
}

2. 使用索引

使用索引来判断当前元素的位置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
#include <vector>

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

    for (size_t i = 0; i < numbers.size(); ++i) {
        // 判断是否是最后一个元素
        if (i == numbers.size() - 1) {
            std::cout << "Last element: " << numbers[i] << std::endl;
        } else {
            std::cout << numbers[i] << ", ";
        }
    }

    return 0;
}

3. 使用标志变量

使用一个标志变量来标记是否是最后一个元素:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <vector>

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

    bool lastElement = false;
    for (const auto& num : numbers) {
        if (lastElement) {
            std::cout << "Last element: " << num << std::endl;
        } else {
            std::cout << num << ", ";
        }
        lastElement = (&num == &numbers.back());
    }

    return 0;
}

这些方法都可以用来判断当前元素是否是最后一个,选择合适的方式取决于具体情况和个人偏好。

C++ 结构体里能够声明自身结构体变量吗

在 C++ 中,结构体是一种用户自定义的数据类型,它可以包含其他数据类型的成员,包括自身结构体类型的成员。这种结构体内部包含自身类型的成员通常被称为递归结构体。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
using namespace std;

// 定义一个包含自身类型成员的结构体
struct Node {
    int data;
    Node* next; // 指向下一个 Node 结构体的指针
};

int main() {
    Node node1;
    Node node2;
    
    node1.data = 10;
    node2.data = 20;
    
    // 将 node2 设置为 node1 的下一个节点
    node1.next = &node2;
    
    // 访问 node1 的下一个节点的数据
    cout << "Data in node1's next node: " << node1.next->data << endl;

    return 0;
}

在上面的例子中,Node 结构体包含一个整型数据成员 data 和一个指向 Node 结构体类型的指针 next。这允许创建一个链表的结构,在其中一个结构体中包含指向另一个结构体的指针,从而形成节点之间的关联。

递归结构体的声明允许结构体类型包含自身类型的成员,但需要注意避免无限递归定义结构体,否则会导致编译器报错或程序陷入无限循环。

C++ 递归结构体是什么

在 C++ 中,递归结构体是指结构体类型内部包含自身类型的成员。这种设计常用于构建具有递归性质的数据结构,例如链表、树等数据结构。

举个例子,考虑一个简单的单向链表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
using namespace std;

// 定义一个包含自身类型成员的结构体
struct Node {
    int data;
    Node* next; // 指向下一个 Node 结构体的指针
};

int main() {
    Node node1;
    Node node2;
    
    node1.data = 10;
    node2.data = 20;
    
    // 将 node2 设置为 node1 的下一个节点
    node1.next = &node2;
    
    // 访问 node1 的下一个节点的数据
    cout << "Data in node1's next node: " << node1.next->data << endl;

    return 0;
}

在这个例子中,Node 结构体包含一个整型数据成员 data 和一个指向 Node 结构体类型的指针 next。这允许创建一个简单的链表结构,其中每个节点都包含指向下一个节点的指针,从而形成节点之间的链接。

递归结构体的使用有助于建立递归性质的数据结构,例如树形结构、图等,这些数据结构的定义通常会依赖于包含自身类型成员的结构体来构建节点或元素之间的关系。

C++ 递归结构体 详解

递归结构体在 C++ 中是指一个结构体内部包含自身类型的成员。这种结构体通常用于构建递归性质的数据结构,比如链表、树、图等。通过递归结构体,可以定义每个节点或元素,这些节点可以直接或间接地指向相同类型的节点,形成递归关系。

下面是一个关于递归结构体的更详细的解释和例子:

递归结构体的特点:

  1. 自引用性质: 在结构体内部,结构体类型可以作为其自身的一个成员,即包含自身类型的成员。
  2. 形成递归关系: 这种结构允许每个实例都能持有另一个相同类型的实例,或者持有指向另一个相同类型的实例的指针。

例子:单链表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <iostream>
using namespace std;

// 定义递归结构体 Node,表示链表节点
struct Node {
    int data;
    Node* next; // 指向下一个 Node 结构体的指针
};

int main() {
    Node node1, node2, node3;

    node1.data = 10;
    node2.data = 20;
    node3.data = 30;

    // 构建链表
    node1.next = &node2;
    node2.next = &node3;
    node3.next = nullptr; // 最后一个节点指向空指针,表示链表结束

    // 遍历链表并输出节点的数据
    Node* current = &node1; // 从第一个节点开始
    while (current != nullptr) {
        cout << "Node data: " << current->data << endl;
        current = current->next; // 移动到下一个节点
    }

    return 0;
}

在上面的例子中,Node 结构体代表链表的节点。每个节点都包含一个整型数据成员 data 和一个指向下一个 Node 结构体的指针 next。通过指针 next,可以将各个节点连接起来,形成一个链表结构。

递归结构体的使用使得链表的节点可以彼此连接,而且每个节点可以访问其下一个节点,从而形成了数据的递归结构。

递归结构体不仅仅局限于链表,它还可以用于树、图等更复杂的数据结构的构建。这种方式能够清晰地描述节点之间的层次或关系,使得对数据结构的操作更为灵活和便捷。

OpenSSL库中有base64编码示例 详解

在 OpenSSL 库中,base64 编码和解码可以使用其提供的函数进行操作。下面是一个简单的示例,展示了如何使用 OpenSSL 库进行 base64 编码和解码。

请注意,以下示例代码主要用于说明如何使用 OpenSSL 进行 base64 编码和解码。在实际应用中,应考虑错误处理、内存管理和其他安全性方面的注意事项。

Base64 编码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <stdio.h>
#include <openssl/bio.h>
#include <openssl/buffer.h>

// 函数用于进行 base64 编码
char *base64_encode(const unsigned char *input, int length) {
    BIO *bio, *b64;
    BUF_MEM *bufferPtr;

    b64 = BIO_new(BIO_f_base64());
    BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
    bio = BIO_new(BIO_s_mem());
    bio = BIO_push(b64, bio);

    BIO_write(bio, input, length);
    BIO_flush(bio);
    BIO_get_mem_ptr(bio, &bufferPtr);

    char *encoded = (char *)malloc(bufferPtr->length);
    memcpy(encoded, bufferPtr->data, bufferPtr->length - 1);
    encoded[bufferPtr->length - 1] = '\0';

    BIO_free_all(bio);

    return encoded;
}

int main() {
    const char *text = "Hello, this is a message to be encoded in Base64!";
    int length = strlen(text);
    char *encoded = base64_encode((const unsigned char *)text, length);

    printf("Base64 Encoded: %s\n", encoded);

    free(encoded);
    return 0;
}

Base64 解码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <stdio.h>
#include <openssl/bio.h>
#include <openssl/buffer.h>

// 函数用于进行 base64 解码
unsigned char *base64_decode(const char *input, int length) {
    BIO *bio, *b64;
    unsigned char *buffer = (unsigned char *)malloc(length);
    memset(buffer, 0, length);

    b64 = BIO_new(BIO_f_base64());
    BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
    bio = BIO_new_mem_buf((void *)input, length);
    bio = BIO_push(b64, bio);

    BIO_read(bio, buffer, length);

    BIO_free_all(bio);

    return buffer;
}

int main() {
    const char *encoded_text = "SGVsbG8sIHRoaXMgaXMgYSBtZXNzYWdlIHRvIGJlIGVuY29kZWQgaW4gQmFzZTY0IQ==";
    int length = strlen(encoded_text);
    unsigned char *decoded = base64_decode(encoded_text, length);

    printf("Base64 Decoded: %s\n", decoded);

    free(decoded);
    return 0;
}

请注意,这些示例仅包含了基本的错误处理和内存管理。在实际应用中,您可能需要更全面地处理错误情况和内存分配释放,以确保安全性和稳定性。此外,在解码时,请记得释放解码后的内存。

在实际应用中使用 OpenSSL 库进行 base64 编码和解码时,请仔细查阅 OpenSSL 的官方文档以获取更多信息,并遵循最佳实践。

C++ std::string转为小写

在 C++ 中,将 std::string 转换为小写可以通过不同的方法实现。以下是一种常见的方法,使用标准库的 std::transform 函数和 std::tolower 函数来实现这个转换:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include <string>
#include <algorithm> // 包含 transform 函数所需的头文件
#include <cctype>    // 包含 std::tolower 所需的头文件

int main() {
    std::string str = "Hello World";

    // 使用 std::transform 将字符串转换为小写
    std::transform(str.begin(), str.end(), str.begin(),
                   [](unsigned char c) { return std::tolower(c); });

    std::cout << "转换为小写后的字符串:" << str << std::endl;

    return 0;
}

这段代码将字符串 str 转换为小写形式并打印输出。std::transform 函数遍历字符串中的每个字符,并将其作为参数传递给 lambda 表达式,lambda 表达式调用 std::tolower 将字符转换为小写形式。

另外,这段代码中使用了 std::transform 函数,它是标准库中的一个通用算法,能够将指定范围内的元素应用给定的操作,并将结果存储到另一个范围中。在这里,它接受了字符串的起始和结束迭代器作为参数,然后通过 lambda 表达式实现了每个字符的小写转换操作。

请注意,在进行字符大小写转换时,使用 std::tolower 函数时要格外小心。这个函数在处理字符之前将其转换为 unsigned char,以避免出现负数值(EOF)导致的问题。

C++ 避免重复定义问题

在 C++ 中,重复定义问题通常是因为同一个变量、函数或者类在多个源文件中被定义而引起的。为了避免这种问题,可以使用以下方法:

使用头文件保护

在头文件中使用预处理器指令 #ifndef, #define, #endif 来防止头文件被多次包含。

例如,创建一个头文件 example.h,在文件开头加上如下内容:

1
2
3
4
5
6
#ifndef EXAMPLE_H
#define EXAMPLE_H

// 此处放置头文件的内容

#endif // EXAMPLE_H

这样,在包含 example.h 的源文件中,预处理器会检查 EXAMPLE_H 是否已经被定义,避免头文件被多次包含。

使用 extern 关键字声明外部链接

如果需要在多个源文件中使用全局变量或函数,可以在声明时使用 extern 关键字,而在定义时不要重复定义。

示例:

在头文件 example.h 中声明全局变量:

1
2
3
4
5
6
7
8
9
// example.h
#ifndef EXAMPLE_H
#define EXAMPLE_H

extern int globalVariable; // 声明全局变量,不要在头文件中定义

void exampleFunction(); // 函数声明

#endif // EXAMPLE_H

在一个源文件中定义全局变量 globalVariable

1
2
3
4
5
6
7
8
// example.cpp
#include "example.h"

int globalVariable = 0; // 定义全局变量

void exampleFunction() {
    // 函数定义
}

使用命名空间

将代码放入命名空间中有助于避免全局命名冲突。将相关的变量、函数和类放入命名空间,以确保命名唯一性。

1
2
3
4
5
6
7
8
9
10
11
12
// example.h
#ifndef EXAMPLE_H
#define EXAMPLE_H

namespace Example {
    // 在命名空间中声明变量、函数和类
    extern int globalVariable;
    void exampleFunction();
    // ...
}

#endif // EXAMPLE_H
1
2
3
4
5
6
7
8
9
10
11
// example.cpp
#include "example.h"

namespace Example {
    int globalVariable = 0; // 定义全局变量

    void exampleFunction() {
        // 函数定义
    }
    // ...
}

这些方法可以帮助你在 C++ 中避免重复定义的问题,保持代码的可维护性和可扩展性。

在for循环内部使用std::vector::erase()函数 需要注意什么

在使用 std::vector::erase() 函数时,尤其是在循环中,在迭代器失效和容器大小改变的情况下需要特别注意。

  1. 迭代器失效: 当使用 std::vector::erase() 删除元素时,被删除元素之后的所有迭代器都会失效。这意味着,如果在循环中直接使用迭代器来遍历元素,并在循环内部调用了 erase() 函数,会导致迭代器失效,进而可能引发未定义行为或程序崩溃。

  2. 循环范围: 如果在循环中调用了 std::vector::erase(),需要注意循环范围。因为删除元素后,容器的大小会发生改变,循环的终止条件可能会受到影响。这可能导致某些元素被跳过,或者在循环结束后尝试访问不存在的元素,产生未定义行为。

为了避免这些问题,有几种常见的方法:

  • 使用迭代器进行循环: 如果确实需要在循环内部删除元素,可以使用迭代器进行循环遍历,并在删除元素后更新迭代器。这样可以确保在每次删除操作后正确调整迭代器位置。

  • 反向循环: 从容器的末尾向前循环,这样在删除元素后,不会影响前面元素的位置,可以减少迭代器失效的可能性。

以下是一个使用迭代器进行循环并删除元素的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include <vector>

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

    // 使用迭代器进行循环,并在循环内部删除偶数
    for (auto it = numbers.begin(); it != numbers.end();) {
        if (*it % 2 == 0) {
            it = numbers.erase(it); // 删除偶数并更新迭代器
        } else {
            ++it; // 指向下一个元素
        }
    }

    // 输出删除后的结果
    for (const auto& num : numbers) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

这个示例中,使用迭代器进行循环遍历 numbers 向量,并在循环内部删除偶数。在删除操作后,根据删除是否发生,更新迭代器的位置。这样可以避免迭代器失效,并确保正确地删除元素。

C++ 字符串格式化的常用方式 详解

在C++中,有几种常用的方式可以进行字符串格式化,以将不同类型的数据插入到字符串中或按特定格式呈现字符串。下面是一些常见的字符串格式化方式的详解:

  1. C-风格字符串格式化

    在C++中,你可以使用类似C的printf函数或sprintf函数来进行字符串格式化。这些函数使用格式化字符串,其中包含占位符,然后用实际的值替换这些占位符。占位符以百分号(%)开头,后面跟着一个格式规范字符,指示如何格式化数据。例如:

    1
    2
    3
    4
    
    int num = 42;
    char buffer[50];
    sprintf(buffer, "The answer is: %d", num);
    printf("Formatted string: %s\n", buffer);
    

    这种方式非常灵活,但要小心,因为它不提供类型安全,可能会导致安全漏洞。

  2. C++11 标准的字符串流

    C++11引入了std::stringstream,它提供了一种更安全和类型安全的字符串格式化方式。你可以像向流中写入数据一样向std::stringstream写入数据,然后从中提取一个字符串。示例如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    #include <iostream>
    #include <sstream>
    #include <string>
       
    int num = 42;
    std::stringstream ss;
    ss << "The answer is: " << num;
    std::string formattedString = ss.str();
    std::cout << "Formatted string: " << formattedString << std::endl;
    

    这种方式提供了类型安全,允许你将不同类型的数据混合到同一个字符串中。

  3. C++11 及更高版本的字符串插值

    在C++11及更高版本中,你还可以使用字符串插值,这是一种更方便的字符串格式化方式。你可以在字符串字面值前加上R,然后在字符串中使用${}来插入变量或表达式。示例如下:

    1
    2
    3
    4
    5
    
    #include <iostream>
       
    int num = 42;
    std::string formattedString = R"(The answer is: )" + std::to_string(num);
    std::cout << "Formatted string: " << formattedString << std::endl;
    

    这种方式非常简洁,但只能在支持C++11及更高版本的编译器中使用。

  4. 使用格式化库

    如果需要更复杂的字符串格式化,例如支持日期、时间、货币等多种格式,你可以考虑使用C++中的格式化库,如Boost.Format或fmtlib。这些库提供了丰富的格式化选项,使你能够以更高级的方式进行字符串格式化。

每种方法都有其优缺点,选择哪种方法取决于具体的需求和项目要求。如果只需要进行简单的格式化,使用C++标准库的方法通常足够,而对于更复杂的需求,可能需要考虑使用格式化库或其他自定义解决方案。

255.255.255.0 删除”.” C++

要从字符串 “255.255.255.0” 中删除点号(”.”),你可以使用 C++ 中的字符串操作。下面是一个示例代码,演示如何删除点号:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
#include <string>

int main() {
    std::string subnetMask = "255.255.255.0";

    // 删除字符串中的点号
    subnetMask.erase(std::remove(subnetMask.begin(), subnetMask.end(), '.'), subnetMask.end());

    std::cout << "删除点号后的字符串为: " << subnetMask << std::endl;

    return 0;
}

这段代码使用了 std::removestd::string::erase 函数。std::remove 函数会将要删除的字符移动到字符串的末尾,并返回一个指向新末尾之后第一个元素的迭代器。然后,std::string::erase 函数用于擦除这些元素,实现了从字符串中删除点号的操作。

运行此代码会输出:删除点号后的字符串为: 2552552550

获取目录下所有文件名 C++11 linux

您好!以下是在Linux系统下使用C++11获取目录下所有文件名的代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <iostream>
#include <dirent.h>
#include <vector>
#include <string>

std::vector<std::string> get_files(const char *src_dir) {
    std::vector<std::string> result;
    DIR *dir = opendir(src_dir);
    if (dir == NULL) {
        std::cout << "Error: " << src_dir << " is not a directory or not exist!" << std::endl;
        return result;
    }
    struct dirent *d_ent = NULL;
    while ((d_ent = readdir(dir)) != NULL) {
        if (d_ent->d_type != DT_DIR) {
            result.push_back(std::string(d_ent->d_name));
        }
    }
    closedir(dir);
    return result;
}

int main() {
    std::vector<std::string> files = get_files("/path/to/directory");
    for (const auto &file : files) {
        std::cout << file << std::endl;
    }
    return 0;
}

这段代码使用了opendir()readdir()函数来打开和读取目录。opendir()函数打开一个目录,返回一个指向DIR类型的指针。readdir()函数读取目录中的下一个条目,返回一个指向dirent类型的指针。d_type成员变量可以用来判断当前条目是文件还是目录。如果是文件,则将文件名存储到std::vector<std::string>类型的容器中。

希望这可以帮助到您!

源: 与必应的对话, 12/19/2023 (1) c++在linux系统下实现获取目录下所有文件名操作 - nickeman - 博客园. https://www.cnblogs.com/nickman/p/14029064.html. (2) c++在linux系统下实现获取目录下所有文件名操作 - nickeman - 博客园. https://bing.com/search?q=%e8%8e%b7%e5%8f%96%e7%9b%ae%e5%bd%95%e4%b8%8b%e6%89%80%e6%9c%89%e6%96%87%e4%bb%b6%e5%90%8d+C%2b%2b11+linux. (3) 【Linux编程】C/C++获取目录下文件或目录 | 神奕的博客. http://songlee24.github.io/2014/09/20/linux-get-directory/. (4) 如何在 C++ 中获取目录中的文件列表 | D栈 - Delft Stack. https://www.delftstack.com/zh/howto/cpp/how-to-get-list-of-files-in-a-directory-cpp/. (5) undefined. https://bing.com/search?q=.

HTTP 请求路径使用递归的方法取出字段 C++

使用递归方法从 HTTP 请求路径中提取字段的示例代码如下(C++):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream>
#include <string>
#include <vector>

void extractFields(const std::string& path, std::vector<std::string>& fields) {
    // 找到路径中第一个斜杠的位置
    size_t pos = path.find('/');

    if (pos != std::string::npos) {
        // 获取斜杠之前的字段
        std::string field = path.substr(0, pos);
        fields.push_back(field);

        // 递归调用以提取下一个字段
        extractFields(path.substr(pos + 1), fields);
    } else {
        // 如果没有斜杠,则将路径中剩余部分作为最后一个字段添加到列表中
        fields.push_back(path);
    }
}

int main() {
    std::string httpPath = "/api/user/profile";
    std::vector<std::string> extractedFields;

    // 提取字段
    extractFields(httpPath, extractedFields);

    // 输出提取的字段
    std::cout << "Extracted fields:" << std::endl;
    for (const std::string& field : extractedFields) {
        std::cout << field << std::endl;
    }

    return 0;
}

这段代码将以递归方式解析 HTTP 请求路径,并将提取的字段存储在字符串向量中。你可以替换 httpPath 变量的内容来测试不同的路径。

C++ std::find_if()在std::map容器中查找

当使用 std::map 容器时,可以使用 std::find_if 来查找满足特定条件的元素。请注意,std::map 是一个关联容器,其中元素以键值对的形式存储。

假设我们有一个 std::map 容器,我们想根据某些条件查找其中的元素。下面是一个例子,展示了如何使用 std::find_ifstd::map 中查找满足特定条件的元素:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
#include <map>
#include <algorithm>

int main() {
    std::map<int, std::string> myMap;

    // 向map中添加一些元素
    myMap[1] = "One";
    myMap[2] = "Two";
    myMap[3] = "Three";
    myMap[4] = "Four";
    myMap[5] = "Five";

    // 定义一个lambda表达式作为查找条件
    auto findCondition = [](const std::pair<int, std::string>& element) {
        return element.second == "Three"; // 查找值为 "Three" 的元素
    };

    // 使用 std::find_if 查找满足条件的元素
    auto it = std::find_if(myMap.begin(), myMap.end(), findCondition);

    // 检查是否找到了元素
    if (it != myMap.end()) {
        std::cout << "Element found: Key = " << it->first << ", Value = " << it->second << std::endl;
    } else {
        std::cout << "Element not found." << std::endl;
    }

    return 0;
}

在这个例子中,我们使用了一个 lambda 表达式 findCondition,它检查 std::pair<int, std::string> 类型的元素中的第二个值是否为 “Three”。然后,我们使用 std::find_ifstd::map 中查找满足条件的元素。

请注意,这个示例中的查找条件是基于值来进行匹配的。您可以根据需要调整 lambda 表达式中的条件来进行不同的查找操作,比如基于键来查找等。

C++中的类成员函数当线程函数

  • C++类成员函数使用时,都会隐式传递一个this指针给该函数,this指针指向该类的对象。函数体可以通过显示调用该指针或直接访问类内成员。
  • 回调函数是通过指针调用的函数,最常使用的回调函数就是在创建线程时,以一个函数指针以及传递给这个函数多个参数来调用线程函数来创建线程。
  • 那么一般的类成员函数是不能用作回调函数的,因为库函数在使用回调函数时,都会传递指定的符合回调函数声明的的参数给回调函数,而类成员函数隐式包含一个this指针参数,所以把类成员函数当作回调函数编译时因为参数不匹配会出错。
  • std::thread,它的第一个参数为函数指针,在c++中这样是获取不到其成员函数的指针,所以会报错。

解决方法一

  • 把成员函数设成静态成员函数,不属于某个对象,属于整个类,没有this指针。但是静态成员函数并不能使用非静态的成员变量(因为它没有某个具体对象的this指针),可通过对象或者类指针调用。

解决方法二

  • 把成员函数声明为友元函数,没有this指针,但是能够访问类的成员变量 在C++中,友元函数是一个能够访问类的私有成员的非成员函数。如果你想要将一个友元函数作为线程函数,你可以使用C++11引入的线程库来实现。以下是一个简单的例子,演示了如何将友元函数作为线程函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <iostream>
#include <thread>
#include <mutex>

class MyClass {
private:
    int privateData;
    std::mutex dataMutex;

public:
    MyClass(int data) : privateData(data) {}

    friend void friendThreadFunction(MyClass& obj); // 友元函数声明

    void displayData() {
        std::lock_guard<std::mutex> lock(dataMutex);
        std::cout << "Private data: " << privateData << std::endl;
    }
};

// 友元函数定义
void friendThreadFunction(MyClass& obj) {
    std::lock_guard<std::mutex> lock(obj.dataMutex);
    obj.privateData += 10;
    obj.displayData();
}

int main() {
    MyClass myObject(5);

    // 创建线程并将友元函数作为线程函数
    std::thread myThread(friendThreadFunction, std::ref(myObject));

    // 主线程执行其他任务
    for (int i = 0; i < 3; ++i) {
        myObject.displayData();
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }

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

    return 0;
}

在这个例子中,MyClass包含一个私有成员privateData,并有一个友元函数friendThreadFunction,该函数可以访问MyClass的私有成员。在main函数中,创建了一个MyClass对象,并启动了一个新线程,将friendThreadFunction作为线程函数,并传递了myObject的引用。主线程执行一些任务,而新线程在后台修改了privateData的值,然后通过调用displayData函数显示了修改后的数据。

请注意,在使用线程时,确保在访问共享数据时使用适当的同步机制,如互斥锁(std::mutex)以防止竞态条件。在上述例子中,使用了std::lock_guard来确保对privateData的访问是线程安全的。