C/C++ Linux 开发教程(GCC + CMake) / 现代 C++(C++11/14/17/20)
现代 C++(C++11/14/17/20)
1. auto 类型推断(C++11)
auto 让编译器自动推导变量类型,减少冗余代码。
#include <iostream>
#include <vector>
#include <map>
#include <string>
int main() {
// 基本用法
auto i = 42; // int
auto d = 3.14; // double
auto s = std::string("hello"); // std::string
auto p = &i; // int*
std::cout << "i=" << i << " d=" << d << " s=" << s << "\n";
// 容器迭代器(不再需要写冗长的类型名)
std::map<std::string, std::vector<int>> data;
data["scores"] = {90, 85, 95};
for (auto it = data.begin(); it != data.end(); ++it) {
std::cout << it->first << ": ";
for (auto val : it->second) {
std::cout << val << " ";
}
std::cout << "\n";
}
// C++14: auto 函数返回类型
auto multiply = [](auto a, auto b) { return a * b; };
std::cout << multiply(3, 4) << "\n"; // 12
std::cout << multiply(2.5, 4.0) << "\n"; // 10.0
// 注意:auto 不适合需要明确类型的场景
auto x = {1, 2, 3}; // std::initializer_list<int>,不是 std::vector!
return 0;
}
⚠️ 注意:
auto x = {1, 2, 3}推导为std::initializer_list<int>,而非std::vector<int>。
2. 范围 for 循环(C++11)
#include <iostream>
#include <vector>
#include <string>
#include <map>
int main() {
// 基本遍历
std::vector<int> nums = {1, 2, 3, 4, 5};
for (auto n : nums) { // 值拷贝
std::cout << n << " ";
}
std::cout << "\n";
for (const auto& n : nums) { // const 引用(推荐只读遍历)
std::cout << n << " ";
}
std::cout << "\n";
for (auto& n : nums) { // 可变引用
n *= 2;
}
for (auto&& n : nums) { // 万能引用(通用写法)
std::cout << n << " ";
}
std::cout << "\n";
// 初始化列表
for (auto v : {10, 20, 30, 40, 50}) {
std::cout << v << " ";
}
std::cout << "\n";
// map 遍历(结构化绑定)
std::map<std::string, int> ages = {{"Alice", 25}, {"Bob", 30}};
for (const auto& [name, age] : ages) {
std::cout << name << " is " << age << "\n";
}
// C++20:初始化语句
std::vector values = {1, 2, 3, 4, 5};
// for (auto i = 0; auto& v : values) { // C++20
// std::cout << i++ << ": " << v << "\n";
// }
return 0;
}
3. Lambda 表达式
#include <iostream>
#include <algorithm>
#include <vector>
#include <functional>
int main() {
std::vector<int> v = {5, 3, 1, 4, 2};
// C++11 基本 Lambda
std::sort(v.begin(), v.end(), [](int a, int b) { return a > b; });
// C++14 泛型 Lambda
auto add = [](auto a, auto b) { return a + b; };
std::cout << add(3, 4) << "\n"; // 7
std::cout << add(1.5, 2.5) << "\n"; // 4.0
// C++14 初始化捕获(移动捕获)
auto ptr = std::make_unique<int>(42);
auto lambda = [p = std::move(ptr)]() {
std::cout << "值: " << *p << "\n";
};
lambda();
// C++17 constexpr Lambda
constexpr auto square = [](int n) { return n * n; };
static_assert(square(5) == 25);
// C++20 模板 Lambda
// auto print_anything = []<typename T>(const T& v) {
// std::cout << v << "\n";
// };
// C++20 初始化捕获带包展开
// auto factory = [args...]<typename T>() { return T(args...); };
// Lambda 作为回调
std::function<int(int, int)> ops[] = {
[](int a, int b) { return a + b; },
[](int a, int b) { return a * b; },
[](int a, int b) { return a - b; }
};
for (auto& op : ops) {
std::cout << op(10, 3) << " "; // 13 30 7
}
std::cout << "\n";
// IIFE(立即调用 Lambda 表达式)
auto result = [](int x) {
return x * x + 2 * x + 1;
}(5);
std::cout << "f(5) = " << result << "\n"; // 36
return 0;
}
4. 初始化列表 initializer_list(C++11)
#include <iostream>
#include <vector>
#include <string>
#include <initializer_list>
class Config {
std::string name_;
std::vector<std::string> values_;
public:
// 接受初始化列表的构造函数
Config(const std::string& name, std::initializer_list<std::string> values)
: name_(name), values_(values) {}
void print() const {
std::cout << name_ << ": ";
for (const auto& v : values_) {
std::cout << "[" << v << "] ";
}
std::cout << "\n";
}
};
// 统一初始化语法
struct Point {
double x;
double y;
double z;
};
int main() {
// 统一初始化(花括号初始化)
int a{42};
double b{3.14};
std::string s{"hello"};
std::vector<int> v{1, 2, 3, 4, 5};
// 聚合初始化
Point p{1.0, 2.0, 3.0};
std::cout << "p = (" << p.x << ", " << p.y << ", " << p.z << ")\n";
// 自定义类使用初始化列表
Config config("server", {"localhost", "8080", "4"});
config.print();
// 不允许窄化转换
// int x{3.14}; // 编译警告/错误:窄化转换
// 空初始化 = 零初始化
int zero{}; // 0
double dz{}; // 0.0
std::string es{}; // ""
std::cout << "zero=" << zero << " dz=" << dz << " es=\"" << es << "\"\n";
return 0;
}
5. 右值引用与移动语义(C++11)
#include <iostream>
#include <string>
#include <vector>
#include <utility>
class Buffer {
int* data_;
size_t size_;
public:
explicit Buffer(size_t n) : data_(new int[n]), size_(n) {
std::cout << "构造: " << size_ << " 元素\n";
}
~Buffer() { delete[] data_; }
// 拷贝构造(深拷贝)
Buffer(const Buffer& other) : data_(new int[other.size_]), size_(other.size_) {
std::copy(other.data_, other.data_ + size_, data_);
std::cout << "拷贝: " << size_ << " 元素\n";
}
// 移动构造(窃取资源)
Buffer(Buffer&& other) noexcept : data_(other.data_), size_(other.size_) {
other.data_ = nullptr;
other.size_ = 0;
std::cout << "移动: " << size_ << " 元素\n";
}
// 移动赋值
Buffer& operator=(Buffer&& other) noexcept {
if (this != &other) {
delete[] data_;
data_ = other.data_;
size_ = other.size_;
other.data_ = nullptr;
other.size_ = 0;
}
std::cout << "移动赋值\n";
return *this;
}
size_t size() const { return size_; }
};
Buffer createBuffer(size_t n) {
return Buffer(n); // NRVO 可能优化掉移动
}
int main() {
Buffer a(1000); // 构造
Buffer b = a; // 拷贝构造
Buffer c = std::move(a); // 移动构造(a 被"掏空")
Buffer d = createBuffer(500); // 可能直接构造(NRVO)
std::cout << "a.size=" << a.size() << " (已移动)\n";
return 0;
}
左值 vs 右值
| 类别 | 说明 | 示例 |
|---|---|---|
| 左值 (lvalue) | 有名字、有地址、可取地址 | 变量、*p、a[i]、赋值表达式 |
| 右值 (rvalue) | 临时对象、字面量、std::move 返回值 | 42、std::string("hi")、std::move(x) |
| 将亡值 (xvalue) | 即将被移动的左值 | std::move(x) 的返回值 |
6. 结构化绑定(Structured Bindings, C++17)
#include <iostream>
#include <tuple>
#include <map>
#include <string>
#include <array>
struct Person {
std::string name;
int age;
std::string city;
};
std::tuple<int, double, std::string> getData() {
return {42, 3.14, "hello"};
}
int main() {
// tuple 解构
auto [id, value, label] = getData();
std::cout << "id=" << id << " value=" << value << " label=" << label << "\n";
// pair 解构
std::map<std::string, int> scores = {{"Alice", 95}, {"Bob", 87}};
for (const auto& [name, score] : scores) {
std::cout << name << ": " << score << "\n";
}
// 数组解构
int arr[] = {10, 20, 30};
auto [x, y, z] = arr;
std::cout << x << ", " << y << ", " << z << "\n";
// 结构体解构
Person p{"Alice", 25, "Beijing"};
auto [name, age, city] = p;
std::cout << name << ", " << age << "岁, " << city << "\n";
// 嵌套结构化绑定
std::map<std::string, std::pair<int, int>> stats = {
{"A", {100, 200}},
{"B", {300, 400}}
};
for (const auto& [key, [min_val, max_val]] : stats) {
std::cout << key << ": [" << min_val << ", " << max_val << "]\n";
}
// if 初始化语句 (C++17)
if (auto [iter, success] = scores.insert({"Charlie", 92}); success) {
std::cout << "插入成功: " << iter->first << "\n";
}
return 0;
}
7. std::optional / variant / any(C++17)
std::optional
#include <iostream>
#include <optional>
#include <string>
std::optional<int> parse_int(const std::string& s) {
try {
return std::stoi(s);
} catch (...) {
return std::nullopt;
}
}
std::optional<std::string> find_env(const char* name) {
const char* val = std::getenv(name);
if (val) return std::string(val);
return std::nullopt;
}
int main() {
auto result = parse_int("42");
if (result) {
std::cout << "解析成功: " << *result << "\n";
}
auto bad = parse_int("abc");
std::cout << "has_value: " << std::boolalpha << bad.has_value() << "\n";
// value_or 提供默认值
int val = parse_int("xyz").value_or(-1);
std::cout << "默认值: " << val << "\n";
// optional 用于可能失败的操作
auto env = find_env("HOME");
if (env) {
std::cout << "HOME=" << *env << "\n";
}
return 0;
}
std::variant
#include <iostream>
#include <variant>
#include <string>
#include <vector>
using Value = std::variant<int, double, std::string>;
void print(const Value& v) {
std::visit([](const auto& val) {
std::cout << val << " ";
}, v);
}
int main() {
std::vector<Value> data;
data.push_back(42);
data.push_back(3.14);
data.push_back(std::string("hello"));
for (const auto& v : data) {
print(v);
}
std::cout << "\n";
// 获取值
Value v = 42;
std::cout << "int: " << std::get<int>(v) << "\n";
// 安全获取
if (auto* p = std::get_if<int>(&v)) {
std::cout << "是 int: " << *p << "\n";
} else {
std::cout << "不是 int\n";
}
// index()
std::cout << "类型索引: " << v.index() << "\n"; // 0 = int
return 0;
}
std::any
#include <iostream>
#include <any>
#include <string>
int main() {
std::any a = 42;
std::cout << "int: " << std::any_cast<int>(a) << "\n";
a = std::string("hello");
std::cout << "string: " << std::any_cast<std::string>(a) << "\n";
// 类型检查
std::cout << "type: " << a.type().name() << "\n";
// 安全转换(失败抛异常)
try {
auto val = std::any_cast<int>(a); // a 是 string,会抛异常
} catch (const std::bad_any_cast& e) {
std::cerr << "转换失败: " << e.what() << "\n";
}
return 0;
}
| 工具 | 类型安全 | 适用场景 |
|---|---|---|
optional<T> | 固定类型 | 值可能存在或不存在 |
variant<Ts...> | 编译期类型集合 | 类型安全的联合体 |
any | 运行时类型擦除 | 完全动态类型 |
8. 文件系统 std::filesystem(C++17)
#include <iostream>
#include <filesystem>
#include <fstream>
namespace fs = std::filesystem;
int main() {
// 创建临时目录
fs::path dir = "/tmp/cpp_demo";
fs::create_directories(dir);
// 创建文件
std::ofstream(dir / "file1.txt") << "Hello";
std::ofstream(dir / "file2.txt") << "World";
fs::create_directories(dir / "subdir");
// 遍历目录
std::cout << "目录内容:\n";
for (const auto& entry : fs::directory_iterator(dir)) {
std::cout << " " << entry.path().filename()
<< (entry.is_directory() ? "/" : "")
<< (entry.is_regular_file() ?
" (" + std::to_string(entry.file_size()) + " bytes)" : "")
<< "\n";
}
// 递归遍历
std::cout << "\n递归遍历:\n";
for (const auto& entry : fs::recursive_directory_iterator(dir)) {
std::cout << " " << fs::relative(entry.path(), dir) << "\n";
}
// 路径操作
fs::path p = "/home/user/documents/report.pdf";
std::cout << "\n路径分解:\n";
std::cout << " root: " << p.root_path() << "\n";
std::cout << " parent: " << p.parent_path() << "\n";
std::cout << " filename: " << p.filename() << "\n";
std::cout << " stem: " << p.stem() << "\n";
std::cout << " extension: " << p.extension() << "\n";
// 文件状态
auto status = fs::status(dir);
std::cout << "\n目录状态: " << static_cast<int>(status.type()) << "\n";
// 磁盘空间
auto space = fs::space("/");
std::cout << "可用空间: " << space.available / (1024 * 1024 * 1024) << " GB\n";
// 清理
fs::remove_all(dir);
std::cout << "已清理临时目录\n";
return 0;
}
编译需要链接 -lstdc++fs(GCC < 9):
g++ -std=c++17 -o demo demo.cpp && ./demo
9. 格式化 std::format(C++20)
#include <iostream>
#include <format>
#include <string>
int main() {
// 基本格式化
std::string s = std::format("Hello, {}!", "World");
std::cout << s << "\n";
// 数字格式化
std::cout << std::format("整数: {:10d}\n", 42); // 右对齐,宽度10
std::cout << std::format("填充: {:*<10d}\n", 42); // 左对齐,* 填充
std::cout << std::format("居中: {:*^10d}\n", 42); // 居中
std::cout << std::format("浮点: {:.3f}\n", 3.14159); // 3位小数
std::cout << std::format("科学: {:.2e}\n", 1234.5); // 科学记数法
std::cout << std::format("十六: {:08x}\n", 255); // 十六进制,前导零
std::cout << std::format("二进: {:08b}\n", 42); // 二进制
// 位置参数
std::cout << std::format("{1} 说 {0}\n", "你好", "Alice");
// 自定义类型格式化
struct Point {
double x, y;
};
// 需要特化 std::formatter(此处仅展示概念)
return 0;
}
⚠️ 注意:GCC 13+ 完整支持
std::format。较早版本可使用{fmt}库作为替代。
10. 协程简介(C++20)
#include <iostream>
#include <coroutine>
#include <optional>
// 简单的生成器(Generator)
template <typename T>
struct Generator {
struct promise_type {
T current_value;
Generator get_return_object() {
return Generator{
std::coroutine_handle<promise_type>::from_promise(*this)};
}
std::suspend_always initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
std::suspend_always yield_value(T value) {
current_value = std::move(value);
return {};
}
void return_void() {}
void unhandled_exception() { std::terminate(); }
};
std::coroutine_handle<promise_type> handle;
~Generator() {
if (handle) handle.destroy();
}
// 获取下一个值
std::optional<T> next() {
if (!handle || handle.done()) return std::nullopt;
handle.resume();
if (handle.done()) return std::nullopt;
return handle.promise().current_value;
}
};
// 协程函数:无限斐波那契生成器
Generator<int> fibonacci() {
int a = 0, b = 1;
while (true) {
co_yield a; // 暂停并返回值
auto temp = a + b;
a = b;
b = temp;
}
}
// 协程函数:范围生成器
Generator<int> range(int start, int end, int step = 1) {
for (int i = start; i < end; i += step) {
co_yield i;
}
}
int main() {
// 使用斐波那契生成器
auto fib = fibonacci();
std::cout << "斐波那契前 10 个: ";
for (int i = 0; i < 10; ++i) {
if (auto val = fib.next()) {
std::cout << *val << " ";
}
}
std::cout << "\n";
// 使用范围生成器
auto r = range(0, 20, 3);
std::cout << "range(0,20,3): ";
while (auto val = r.next()) {
std::cout << *val << " ";
}
std::cout << "\n";
return 0;
}
编译需要 GCC 10+ 或 Clang 14+:
g++ -std=c++20 -fcoroutines -o demo demo.cpp && ./demo
⚠️ 注意:
co_await、co_yield、co_return是协程关键字。GCC 10+ 用-fcoroutines启用。
11. 模块简介(C++20)
// math_utils.cppm(模块接口文件)
// export module math_utils;
//
// export int add(int a, int b) { return a + b; }
// export int multiply(int a, int b) { return a * b; }
//
// export template <typename T>
// T max_val(T a, T b) { return a > b ? a : b; }
// main.cpp(使用模块)
// import math_utils;
// import <iostream>;
//
// int main() {
// std::cout << add(3, 4) << "\n";
// std::cout << multiply(5, 6) << "\n";
// return 0;
// }
模块编译示意:
# GCC 编译模块(实验性支持)
# g++ -std=c++20 -fmodules-ts -c math_utils.cppm -o math_utils.gcm
# g++ -std=c++20 -fmodules-ts main.cpp math_utils.gcm -o demo
💡 提示:GCC 对 C++20 模块的支持仍在完善中。生产环境建议继续使用头文件 +
#pragma once。Clang 和 MSVC 的模块支持更为成熟。
C++ 标准演进速查表
| 标准 | 年份 | 关键特性 |
|---|---|---|
| C++11 | 2011 | auto, lambda, 右值引用, 智能指针, 范围 for |
| C++14 | 2014 | 泛型 lambda, 返回类型推导, 二进制字面量 |
| C++17 | 2017 | 结构化绑定, optional/variant/any, filesystem, constexpr if |
| C++20 | 2020 | Concepts, Ranges, Coroutines, Modules, format |
| C++23 | 2023 | expected, print, mdspan, stacktrace |
扩展阅读
- cppreference — C++11/14/17/20 features
- C++ Core Guidelines — Modern C++
- Effective Modern C++ (Scott Meyers)
- C++ Weekly (Jason Turner) YouTube
- Compiler Explorer