1. C++ 中的列表、字符串
为了寻找 Python 中 list 和 str 的对应 C++ 平替,我们使用 vector 和 string。在 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 list | C++ 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 str | C++ 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 二维列表的对比
| Python | C++ |
|---|---|
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 指针的关键场景:
setLearningRate函数:参数名learning_rate与成员变量同名,必须使用this->learning_rate来区分- 构造函数中:虽然使用了初始化列表,但在函数体内访问成员变量时显式使用了
this指针 - 所有成员函数:显式使用
this指针访问成员变量,提高代码可读性和明确性
这种写法虽然不是必需的(除了参数名冲突的情况),但能让代码更加清晰地表明正在访问的是类的成员变量。
4. Python 中 集合 set 与 字典 dict 的平替
在 C++ 中,集合和字典可以使用 std::set 和 std::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 set | C++ std::set | Python dict | C++ 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'] = 3 | m["c"] = 3; |
| 检查成员 | 4 in s | s.find(4) != s.end() | 'a' in d | m.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::map 的 find 方法来检查某个键是否存在。如果键存在,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++ 也支持直接返回 set、string 等 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 w11. C++ 中的对字符串排序
C++ 中可以使用 std::sort 函数对字符串进行排序。std::sort 是一个通用的排序算法,可以对任何可迭代的容器进行排序,包括字符串。
string str = "hello world";
std::sort(str.begin(), str.end()); // 对字符串进行排序
std::cout << str << std::endl; // 输出排序后的字符串 " dehllloorw"
