3368 个字词
17 分钟
C++ Misc
首次发布: 2025-07-19
... 次访问

1. C++ 中的列表、字符串

为了寻找 Python 中 liststr 的对应 C++ 平替,我们使用 vectorstring。在 C++ 中,vector 是一个动态数组,可以存储任意类型的元素,并且可以动态调整大小。string 则是一个用于处理文本的类,提供了许多方便的字符串操作方法。

#include <vector>

vector<int> nums = {1, 2, 3, 4, 5}; // 初始化一个整数 vector 
vector<int> empty; // 空 vector
vector<int> list(nums.size()); // 创建一个与 nums 相同大小的空 vector

#include <string>
string str = "Hello, World!"; // 初始化一个字符串
string emptyStr; // 空字符串
string subStr = str.substr(0, 5); // 获取子字符串 "Hello"

list 与 vector 常用操作对比

Python listC++ vector
len(list)nums.size()
list[i:j]vector<int>(nums.begin()+i, nums.begin()+j)
list.append(x)nums.push_back(x)
list.insert(i, x)nums.insert(nums.begin()+i, x)

str 与 string 常用操作对比

Python strC++ string
len(str)str.size()str.length()
str[i:j]str.substr(i, j-i)
str += 'c'str += 'c'str.push_back('c')
str.insert(i, "text")str.insert(i, "text")

示例代码

// 获取长度
int vecSize = nums.size();
int strLen = str.length();

// 获取子串/子列表
vector<int> subVec(nums.begin()+1, nums.begin()+4); // 获取索引 1-3 的元素
string subString = str.substr(7, 5); // 从索引 7 开始取 5 个字符 "World"

// 添加元素
nums.push_back(6); // 在末尾添加元素 6
str += " Welcome!"; // 在字符串末尾添加文本

// 插入元素
nums.insert(nums.begin()+2, 10); // 在索引 2 处插入元素 10
str.insert(5, ", C++"); // 在索引 5 处插入字符串

2. 创建指定大小的二维 vector

在 C++ 中,我们可以创建指定行数和列数的二维 vector,类似于 Python 中的二维列表。

#include <vector>

// 创建一个 3x4 的二维 vector,初始值为 0
vector<vector<int>> matrix(3, vector<int>(4, 0));

// 创建一个 m x n 的二维 vector,初始值为某个特定值
int m = 5, n = 6;
vector<vector<int>> grid(m, vector<int>(n, -1)); // 5x6,初始值为 -1

// 创建空的二维 vector,然后动态添加行
vector<vector<int>> dynamicMatrix;
for (int i = 0; i < 3; i++) {
    dynamicMatrix.push_back(vector<int>(4, 0)); // 添加一行,包含 4 个元素,初始值为 0
}

访问和修改二维 vector

// 访问元素
int value = matrix[1][2]; // 获取第 1 行第 2 列的元素

// 修改元素
matrix[0][3] = 42; // 将第 0 行第 3 列设置为 42

// 获取维度信息
int rows = matrix.size(); // 行数
int cols = matrix[0].size(); // 列数(假设所有行都有相同的列数)

与 Python 二维列表的对比

PythonC++
matrix = [[0] * 4 for _ in range(3)]vector<vector<int>> matrix(3, vector<int>(4, 0))
matrix[i][j]matrix[i][j]
len(matrix)matrix.size()
len(matrix[0])matrix[0].size()

3. C++ 中类内函数访问

在 C++ 中,类内函数可以通过 this 指针访问类的成员变量和其他成员函数。this 指针指向当前对象的地址。

以下是使用标准模板库实现的神经网络线性层,包含前向传播、反向传播和参数更新功能。

#include <vector>
#include <random>
#include <iostream>
#include <algorithm>
#include <numeric>

class LinearLayer {
private:
    std::vector<std::vector<double>> weights;  // 权重矩阵 [out_size x in_size]
    std::vector<double> bias;                  // 偏置向量 [out_size]
    std::vector<double> last_input;           // 保存最后一次的输入,用于反向传播
    
    int input_size;
    int output_size;
    double learning_rate;

public:
    // 构造函数 - 参数名与成员变量同名时使用this
    LinearLayer(int input_size, int output_size, double learning_rate = 0.01) 
        : input_size(input_size), output_size(output_size), learning_rate(learning_rate) {
        
        // 使用this指针访问成员变量
        this->weights.resize(this->output_size, std::vector<double>(this->input_size));
        this->bias.resize(this->output_size);
        
        // Xavier初始化
        std::random_device rd;
        std::mt19937 gen(rd());
        double stddev = std::sqrt(2.0 / (this->input_size + this->output_size));
        std::normal_distribution<double> dist(0.0, stddev);
        
        // 使用this指针初始化权重
        for (int i = 0; i < this->output_size; i++) {
            for (int j = 0; j < this->input_size; j++) {
                this->weights[i][j] = dist(gen);
            }
        }
        
        // 使用this指针初始化偏置为0
        std::fill(this->bias.begin(), this->bias.end(), 0.0);
    }
    
    // 前向传播
    std::vector<double> forward(const std::vector<double>& input) {
        if (input.size() != this->input_size) {
            throw std::invalid_argument("Input size mismatch");
        }
        
        // 使用this指针保存输入用于反向传播
        this->last_input = input;
        
        std::vector<double> output(this->output_size, 0.0);
        
        // 使用this指针计算 output = weights * input + bias
        for (int i = 0; i < this->output_size; i++) {
            output[i] = this->bias[i];
            for (int j = 0; j < this->input_size; j++) {
                output[i] += this->weights[i][j] * input[j];
            }
        }
        
        return output;
    }
    
    // 反向传播
    std::vector<double> backward(const std::vector<double>& grad_output) {
        if (grad_output.size() != this->output_size) {
            throw std::invalid_argument("Gradient output size mismatch");
        }
        
        // 使用this指针计算输入的梯度
        std::vector<double> grad_input(this->input_size, 0.0);
        for (int j = 0; j < this->input_size; j++) {
            for (int i = 0; i < this->output_size; i++) {
                grad_input[j] += this->weights[i][j] * grad_output[i];
            }
        }
        
        // 使用this指针更新权重和偏置
        for (int i = 0; i < this->output_size; i++) {
            // 更新偏置
            this->bias[i] -= this->learning_rate * grad_output[i];
            
            // 更新权重
            for (int j = 0; j < this->input_size; j++) {
                this->weights[i][j] -= this->learning_rate * grad_output[i] * this->last_input[j];
            }
        }
        
        return grad_input;
    }
    
    // 设置学习率 - 参数名与成员变量同名时使用this
    void setLearningRate(double learning_rate) {
        this->learning_rate = learning_rate;
    }
    
    // 获取权重 - 使用this指针返回成员变量
    const std::vector<std::vector<double>>& getWeights() const {
        return this->weights;
    }
    
    // 获取偏置 - 使用this指针返回成员变量
    const std::vector<double>& getBias() const {
        return this->bias;
    }
    
    // 打印层信息 - 使用this指针访问成员变量
    void printInfo() const {
        std::cout << "Linear Layer: " << this->input_size << " -> " << this->output_size 
                  << ", Learning Rate: " << this->learning_rate << std::endl;
    }
};

// 使用示例
int main() {
    // 创建一个 3输入 -> 2输出 的线性层
    LinearLayer layer(3, 2, 0.01);
    
    // 输入数据
    std::vector<double> input = {1.0, 2.0, 3.0};
    
    // 前向传播
    std::vector<double> output = layer.forward(input);
    
    std::cout << "Input: ";
    for (double val : input) std::cout << val << " ";
    std::cout << "\nOutput: ";
    for (double val : output) std::cout << val << " ";
    std::cout << std::endl;
    
    // 模拟反向传播(假设输出梯度)
    std::vector<double> grad_output = {0.5, -0.3};
    std::vector<double> grad_input = layer.backward(grad_output);
    
    std::cout << "Gradient Input: ";
    for (double val : grad_input) std::cout << val << " ";
    std::cout << std::endl;
    
    return 0;
}

使用 this 指针的关键场景:

  1. setLearningRate 函数:参数名 learning_rate 与成员变量同名,必须使用 this->learning_rate 来区分
  2. 构造函数中:虽然使用了初始化列表,但在函数体内访问成员变量时显式使用了 this 指针
  3. 所有成员函数:显式使用 this 指针访问成员变量,提高代码可读性和明确性

这种写法虽然不是必需的(除了参数名冲突的情况),但能让代码更加清晰地表明正在访问的是类的成员变量。

4. Python 中 集合 set 与 字典 dict 的平替

在 C++ 中,集合和字典可以使用 std::setstd::unordered_map 来实现。std::set 是一个不允许重复元素的容器,而 std::map 是一个键值对容器,类似于 Python 的字典。

#include <set>
#include <map>

// 创建一个整数集合
std::set<int> mySet = {1, 2, 3, 4, 5};

// 创建一个空集合
std::set<int> emptySet;

// 向集合中添加元素
mySet.insert(6); // 添加元素 6

// 检查元素是否存在
bool exists = (mySet.find(3) != mySet.end()); // 检查元素 3 是否存在

// 删除元素
mySet.erase(2); // 删除元素 2   

// 获取集合大小
int setSize = mySet.size(); // 获取集合大小

// 创建一个整数到字符串的映射
std::map<int, std::string> myDict;

// 创建一个空字典
std::map<int, std::string> emptyDict;

// 向字典中添加键值对
myDict[1] = "one"; // 添加键 1,值 "one"

// 检查键是否存在
bool keyExists = (myDict.find(2) != myDict.end()); // 检查键 2 是否存在

// 获取值
std::string value = myDict[1]; // 获取键 1 对应的值

// 删除键值对
myDict.erase(1); // 删除键 1 对应的键值对

// 获取字典大小
int dictSize = myDict.size(); // 获取字典大小

// 遍历集合
for (const auto& elem : mySet) {
    std::cout << elem << " "; // 输出集合中的元素
}

// 遍历字典
for (const auto& pair : myDict) {
    std::cout << pair.first << ": " << pair.second << " "; // 输出键值对
}
功能Python setC++ std::setPython dictC++ std::map
创建s = {1, 2, 3}std::set<int> s = {1, 2, 3};d = {'a': 1, 'b': 2}std::map<std::string, int> m = {{"a",1}, {"b",2}};
添加元素s.add(4)s.insert(4);d['c'] = 3m["c"] = 3;
检查成员4 in ss.find(4) != s.end()'a' in dm.find("a") != m.end()
删除元素s.remove(2)s.discard(2)s.erase(2);del d['b']m.erase("b");
大小len(s)s.size()len(d)m.size()
遍历for x in s:for (auto &x : s) { … }for k, v in d.items():for (auto &p : m) { /* p.first, p.second */ }
元素顺序无序自动按升序排序保持插入顺序(Python 3.7+)按 key 自动排序
底层实现哈希表红黑树(或平衡 BST)哈希表红黑树(或平衡 BST)
下标访问不支持不支持支持 d['key']支持 m["key"]

实现 list 向 set 的转换

#include <vector>
#include <set>

std::vector<int> vec = {1, 2, 3, 4, 5, 5}; // 包含重复元素的 vector
std::set<int> uniqueSet(vec.begin(), vec.end()); // 转换为 set,自动去重

删除容器,类似 Python 的 del

#include <set>
std::set<int> mySet = {1, 2, 3, 4, 5};
// 删除整个 set
mySet.clear(); // 清空 set
// 删除 set 对象
mySet.~set(); // 显式调用析构函数(不推荐,通常使用 clear())

vector<int> myVector = {1, 2, 3, 4, 5};
// 删除整个 vector
myVector.clear(); // 清空 vector
// 删除 vector 对象
myVector.~vector(); // 显式调用析构函数(不推荐,通常使用 clear())

5. C++ 中 map 默认值如何初始化为-1

在 C++ 中,std::map 的默认值可以通过在插入时指定默认值来实现。以下是一个示例,展示如何将 std::map 的默认值初始化为 -1。

#include <map>
#include <string>
#include <iostream>

int main() {
    std::map<std::string, int> myMap;
    
    // 访问不存在的键时,会自动创建该键并赋值为 int 的默认值 0
    std::cout << myMap["notExist"] << std::endl; // 输出 0
    
    // 可以使用 [] 操作符手动设置默认值
    std::string key = "newKey";
    if (myMap.find(key) == myMap.end()) {
        myMap[key] = -1; // 手动设置默认值为 -1
    }
}

6. C++ 中实现类似 Python defaultdict 的功能

在 Python 中,defaultdict 允许我们为不存在的键提供默认值。C++ 中没有直接等价物,但可以通过以下方法实现类似功能:

#include <unordered_map>
#include <iostream>

// 方法 1: 使用 [] 运算符和查找
void method1() {
    std::unordered_map<std::string, int> counter;
    
    // 检查键是否存在,不存在则初始化为1
    std::string word = "hello";
    if (counter.find(word) == counter.end()) {
        counter[word] = 1; // 初始值设为1
    } else {
        counter[word]++; // 存在则增加计数
    }
}

// 方法 2: 自定义一个带默认值的包装类
template<typename K, typename V>
class DefaultMap {
private:
    std::unordered_map<K, V> map;
    V defaultValue;

public:
    DefaultMap(V defaultVal) : defaultValue(defaultVal) {}
    
    V& operator[](const K& key) {
        if (map.find(key) == map.end()) {
            map[key] = defaultValue;
        }
        return map[key];
    }
    
    // 其他必要的函数...
};

// 方法 3: 使用 C++17 的 try_emplace
void method3() {
    std::unordered_map<std::string, int> counter;
    
    std::string word = "hello";
    // 尝试插入默认值1,如果键不存在
    auto [it, inserted] = counter.try_emplace(word, 1);
    
    if (!inserted) {
        // 键已存在,增加计数
        it->second++;
    }
}

int main() {
    // 使用自定义的DefaultMap
    DefaultMap<std::string, int> wordCount(1); // 默认值为1
    
    wordCount["apple"]++;  // 第一次访问,设为1,然后+1变成2
    wordCount["banana"]++; // 第一次访问,设为1,然后+1变成2
    wordCount["apple"]++;  // 变成3
    
    std::cout << "apple count: " << wordCount["apple"] << std::endl;    // 输出 3
    std::cout << "banana count: " << wordCount["banana"] << std::endl;  // 输出 2
    std::cout << "cherry count: " << wordCount["cherry"] << std::endl;  // 输出 1 (默认值)
    
    return 0;
}

这些方法可以模拟 Python 的 defaultdict(int)defaultdict(lambda: 1) 的行为,特别适用于计数器和频率统计等场景。

7. C++ 中查找 map 中某个键的值是否存在

在 C++ 中,可以使用 std::mapfind 方法来检查某个键是否存在。如果键存在,find 方法会返回一个指向该键值对的迭代器;如果不存在,则返回 end() 迭代器。

map<std::string, int> myMap = {{"apple", 1}, {"banana", 2}, {"cherry", 3}};

std::string keyToFind = "banana";
auto it = myMap.find(keyToFind);

if (it != myMap.end()) {
    // 键存在,输出对应的值
    std::cout << "Key: " << keyToFind << ", Value: " << it->second << std::endl;
} else {
    // 键不存在
    std::cout << "Key: " << keyToFind << " does not exist." << std::endl;
}

8. C++ 中的 map 如何遍历所有键值对

在 C++ 中,可以使用范围基于的 for 循环或迭代器来遍历 std::map 中的所有键值对。以下是两种常用的方法:

map<std::string, int> myMap = {{"apple", 1}, {"banana", 2}, {"cherry", 3}};
// 方法 1: 使用范围基于的 for 循环
for (const auto& pair : myMap) {
    std::cout << "Key: " << pair.first << ", Value: " << pair.second << std::endl;
}

// 方法 2: 使用迭代器
for (auto it = myMap.begin(); it != myMap.end(); ++it) {
    std::cout << "Key: " << it->first << ", Value: " << it->second << std::endl;
}

9. C++ 中直接返回 vector

vector<int> createVector(int size, int initialValue) {
    if (size <= 0) {
        return {}; // 返回空 vector
    }
    return {initialValue}; // 返回一个包含 initialValue 的 vector
}

这种直接返回 vector 的方式在 C++ 中是合法的,并且会自动处理内存管理。C++11 及以后的版本支持移动语义,这使得返回大型对象(如 vector)时性能更高效。

此外,C++ 会自动根据函数类型,判断 {} 的返回内容和类型。例如,返回 map 时,C++ 会自动推断出返回类型为 std::map

map<int, int> createMap() {
    return {{1, 10}, {2, 20}, {3, 30}}; // 返回一个初始化的 map
}

同理,C++ 也支持直接返回 setstring 等 STL 容器。

set<int> createSet() {
    return {1, 2, 3, 4, 5}; // 返回一个初始化的 set
}

10. C++ 中用多重集分解字符串

在 C++ 中,可以使用 std::multiset 来存储字符串的分解结果。std::multiset 允许存储重复的元素,因此适合用于分解字符串时统计每个字符的出现次数。

string input = "hello world";
std::multiset<char> charSet;
for (char c : input) {
    if (c != ' ') { // 忽略空格
        charSet.insert(c); // 插入字符到 multiset
    }
}
// 输出分解结果
for (const auto& c : charSet) {
    std::cout << c << " ";
}// 输出结果: d e h l l o r w

当然,也可以用 std::set 来存储唯一字符,但如果需要统计每个字符的出现次数,std::multiset 更为合适。

string input = "hello world";
std::set<char> charSet;
for (char c : input) {
    if (c != ' ') { // 忽略空格
        charSet.insert(c); // 插入字符到 set
    }
}
// 输出分解结果
for (const auto& c : charSet) {
    std::cout << c << " ";
}// 输出结果: d e h l o r w

11. C++ 中的对字符串排序

C++ 中可以使用 std::sort 函数对字符串进行排序。std::sort 是一个通用的排序算法,可以对任何可迭代的容器进行排序,包括字符串。

string str = "hello world";
std::sort(str.begin(), str.end()); // 对字符串进行排序
std::cout << str << std::endl; // 输出排序后的字符串 " dehllloorw"

留言板