找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

3668

积分

0

好友

478

主题
发表于 3 小时前 | 查看: 4| 回复: 0

说一个真实情况:很多C++程序员写了好几年代码,用的还是C++98那套思路,顶多加个 auto 装装门面。但Modern C++(C++11起)带来的那些特性,真正用起来之后你会发现——不是写起来更爽,而是少犯错、少写废话、逻辑更清晰。今天这篇,从实际工程出发,带你过一遍那些真正值得用的特性。不讲概念,只讲怎么用、为什么好。

一、C++11:现代C++的起点

1. auto — 让编译器帮你推类型

最常见的写法,比如遍历一个 map

// 以前这样写
std::map<std::string, std::vector<int>>::iterator it = myMap.begin();

// 现在这样
auto it = myMap.begin();

auto 的真正价值不是懒得写类型,而是代码不再与具体类型耦合。 容器换了,类型变了,这行代码不用动。实际工程里,返回值类型复杂的函数、模板代码里,auto 能救命。

2. 范围for循环 — 告别下标越界

std::vector<int> nums = {1, 2, 3, 4, 5};

// 以前
for (int i = 0; i < nums.size(); ++i) { ... }

// 现在
for (auto& n : nums) {
    n *= 2;
}

& 避免拷贝,加 const & 只读访问。这个用熟了,基本不会再写下标循环了。

3. Lambda表达式 — 函数就是数据

C++11最重磅的特性之一。以前要传函数给算法,得单独定义一个函数或者写仿函数,现在:

std::vector<int> v = {3, 1, 4, 1, 5, 9};

std::sort(v.begin(), v.end(), [](int a, int b) {
    return a > b; // 降序
});

更实用的是捕获上下文变量:

int threshold = 5;
auto it = std::find_if(v.begin(), v.end(), [threshold](int x) {
    return x > threshold;
});

线程池、回调、事件系统,Lambda让代码紧凑到极点,逻辑不再散落各处。

4. 智能指针 — 手动 delete 从此绝迹

这不只是语法糖,是工程安全的基石。

// 以前,你得记得delete,还得处理异常安全
Foo* p = new Foo();
// ... 中间可能抛异常 ...
delete p; // 可能执行不到!

// 现在
auto p = std::make_unique<Foo>();
// 离开作用域自动释放,异常也不怕

unique_ptr 独占所有权,shared_ptr 共享所有权,weak_ptr 解决循环引用。 实际工程建议:优先用 unique_ptr,只有真的需要共享时才用 shared_ptr,后者有引用计数开销。

5. 移动语义与右值引用 — 大对象不再白白拷贝

这个改变了C++的性能基因。

std::vector<int> a(1000000, 0);

std::vector<int> b = a;            // 拷贝,复制100万个元素
std::vector<int> c = std::move(a); // 移动,a的内存直接转给c,O(1)
// 此时a变空,c拥有全部数据

往容器里放大对象时同理:

std::string bigStr(1000000, 'x');

vec.push_back(bigStr);               // 拷贝,bigStr还在
vec.push_back(std::move(bigStr));    // 移动,bigStr被掏空,不拷贝内存

自己写类时,记得实现移动构造和移动赋值:

class Buffer {
    Buffer(Buffer&& other) noexcept
        : data_(other.data_), size_(other.size_) {
        other.data_ = nullptr; // 转移资源
        other.size_ = 0;
    }
};

凡是要往容器里 push 大对象,优先用 std::move

6. emplace_back vs push_back

struct Point { int x, y; };
std::vector<Point> pts;

pts.push_back({1, 2});   // 先构造临时对象,再移动
pts.emplace_back(1, 2);  // 直接在容器内构造,少一次移动

差别在数据量大、对象构造代价高的时候会体现出来。

7. nullptr — 彻底告别 NULL

void foo(int);
void foo(int*);

foo(NULL);    // 二义性,编译器可能选错重载
foo(nullptr); // 明确是指针,不会出错

这是一个很小但必须养成的习惯,现代C++代码里不应该出现 NULL

8. overridefinal — 继承不再藏坑

class Base {
    virtual void process(int x);
};

class Derived : public Base {
    void process(int x) override; // 编译器检查:父类有没有这个虚函数?
    // 少写了一个参数、拼错了名字,编译直接报错
};

没有 override 的时候,函数签名写错了,你以为在重写,其实是新函数,运行时行为完全错误,还不报错。

9. constexpr — 把计算搬到编译期

constexpr int fibonacci(int n){
    return n <= 1 ? n : fibonacci(n-1) + fibonacci(n-2);
}

constexpr int val = fibonacci(10); // 编译期算好,运行时是常量

查表、哈希值、协议常量……能 constexpr 的都 constexpr,运行时开销为零。

10. 统一初始化语法 {}

int a{5};
std::vector<int> v{1, 2, 3};
std::map<std::string, int> m{{"a", 1}, {"b", 2}};

struct Point { int x, y; };
Point p{3, 4};

最大的好处是防止窄化转换

int x = 3.14;    // 编译通过,丢失小数
int y{3.14};     // 编译报错,保护你不犯错

11. std::thread — 标准库多线程

#include <thread>

void worker(int id) { /* ... */ }

std::thread t(worker, 42);
t.join();

配合Lambda就更灵活了,结合 std::mutexstd::condition_variable,现代C++的并发基础全在标准库里,不用依赖平台API。

12. std::asyncstd::future — 异步任务拿结果

auto fut = std::async(std::launch::async, []() {
    return computeHeavyResult(); // 另一个线程跑
});

// 主线程干别的事...

int result = fut.get(); // 需要结果时才等待

适合I/O密集、并行计算场景,代码比手写线程简洁得多。

13. tuple + 结构化绑定的前身 tie

std::tuple<int, std::string, double> getData(){
    return {42, "hello", 3.14};
}

int n; std::string s; double d;
std::tie(n, s, d) = getData(); // C++11的解包方式

二、C++14:打磨细节,用起来更顺手

14. 泛型Lambda

auto add = [](auto a, auto b) { return a + b; };

add(1, 2);       // int
add(1.5, 2.5);   // double
add(std::string("hi"), " there"); // string

写工具函数、算法时非常好用,不用为每种类型单独写重载。

15. make_unique

// C++11没有make_unique,要这样写
std::unique_ptr<Foo> p(new Foo(args...));

// C++14终于补上了
auto p = std::make_unique<Foo>(args...);

统一风格,也能防止某些极端情况下的内存泄漏。

16. 返回值类型推导

auto multiply(int a, int b){
    return a * b; // 编译器自动推导返回类型
}

配合模板代码用特别方便,不用写复杂的 decltype 推导式了。

三、C++17:实用特性爆发期

17. 结构化绑定 — 多返回值终于优雅了

std::map<std::string, int> scores;
scores["Alice"] = 95;

// 遍历map,以前要写first/second
for (auto& [name, score] : scores) {
    std::cout << name << ": " << score << "\n";
}

// 函数多返回值
auto [ok, value] = parseConfig("config.json");

这个用起来真的很爽,代码可读性直接上一个台阶。

18. if constexpr — 编译期条件分支

template <typename T>
void print(T val){
    if constexpr (std::is_integral_v<T>){
        std::cout << "整数: " << val;
    } else if constexpr (std::is_floating_point_v<T>) {
        std::cout << "浮点: " << val;
    } else {
        std::cout << "其他类型";
    }
}

不同分支可以有不同的语法要求,编译器只编译满足条件的分支,不像普通 if 两个分支都要能通过编译。

19. std::optional — 告别魔法返回值

以前函数找不到结果,要么返回 -1nullptr,要么抛异常,语义不清晰。

std::optional<User> findUser(int id){
    if (/* 找到了 */) return User{...};
    return std::nullopt; // 明确表示"没有"
}

auto user = findUser(42);
if (user.has_value()) {
    doSomething(*user);
}

// 或者用value_or给默认值
auto name = findUser(42).value_or(defaultUser).name;

语义清晰,调用方一眼知道这个函数可能没有结果。

20. std::variant — 类型安全的union

using Result = std::variant<int, std::string, Error>;

Result process(Input in){
    if (/* 成功 */) return 42;
    if (/* 部分失败 */) return std::string("warning");
    return Error{"something went wrong"};
}

std::visit([](auto&& val) {
    using T = std::decay_t<decltype(val)>;
    if constexpr (std::is_same_v<T, int>) { /* 处理int */ }
    else if constexpr (std::is_same_v<T, std::string>) { /* 处理string */ }
    else { /* 处理Error */ }
}, result);

配合 std::visit,比继承体系轻量得多,适合状态机、解析器、错误处理。

21. std::string_view — 零拷贝字符串处理

// 以前:传string可能触发拷贝
void process(const std::string& s);

// 现在:不管是string、字符串字面量、还是char数组,都能传,不拷贝
void process(std::string_view sv);

// 用法
std::string s = "hello world";
process(s);           // OK
process("hello");     // OK,不拷贝
process(s.substr(0, 5)); // substr返回string,有拷贝;但string_view的substr是零拷贝

处理大量字符串、解析协议时,性能提升明显。

22. if / switch 初始化语句

// 以前:要在外面声明变量,污染外层作用域
auto it = myMap.find(key);
if (it != myMap.end()) { /* 用it */ }
// it还活着,后面可能误用

// 现在:it只活在if块里
if (auto it = myMap.find(key); it != myMap.end()) {
    // 用it
}

作用域控制更精准,变量不会泄漏到不该用的地方。

23. 折叠表达式 — 变参模板从此简单

// 对任意数量参数求和
template <typename... Args>
auto sum(Args... args){
    return (args + ...); // 折叠表达式,一行搞定
}

sum(1, 2, 3, 4, 5); // 15
sum(1.0, 2.5, 3.7); // 7.2

以前写递归展开,现在一行。

四、C++20:质的飞跃

24. Concepts — 模板错误信息终于能看了

template <typename T>
concept Numeric = std::is_arithmetic_v<T>;

template <Numeric T>
T add(T a, T b) { return a + b; }

add(1, 2);        // OK
add("a", "b");    // 编译报错:不满足Numeric约束,错误信息清晰明了

以前 模板 出错,错误信息十几行,看不懂。Concepts直接告诉你哪个约束没满足。

25. Ranges — 管道式数据处理

#include <ranges>
#include <algorithm>

std::vector<int> nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

auto result = nums
    | std::views::filter([](int n) { return n % 2 == 0; })
    | std::views::transform([](int n) { return n * n; })
    | std::views::take(3);
// result: 4, 16, 36(前三个偶数的平方)

惰性求值,链式操作,代码意图一目了然。

26. Coroutines(协程) — 异步代码写成同步风格

Task<std::string> fetchData(std::string url){
    auto response = co_await httpGet(url);    // 挂起,不阻塞线程
    auto parsed = co_await parseJson(response);
    co_return parsed.data;
}

网络库、数据库访问、高并发服务端,协程让异步代码的可读性接近同步代码,是高性能服务端的趋势。

27. std::format — 终于有了Python那样的字符串格式化

std::string s = std::format("用户: {}, 分数: {:.2f}", username, score);
// 比sprintf安全,比ostringstream简洁

告别 sprintf 的不安全,告别 ostringstream 的啰嗦。

28. std::span — 安全的数组视图

void process(std::span<int> data){
    for (auto& n : data) { /* ... */ }
}

int arr[] = {1, 2, 3, 4, 5};
std::vector<int> vec = {1, 2, 3};

process(arr);          // OK
process(vec);          // OK
process({arr, 3});     // 只看前3个,OK

统一了对各种“连续内存序列”的处理,不再需要指针+长度两个参数分开传。

总结

把上面这些分个层次来看:

  • 必须掌握、日常高频auto、范围for、Lambda、智能指针、移动语义、overridenullptrconstexpr
  • 工程实战必备string_viewoptional、结构化绑定、if初始化语句、emplace_back
  • 进阶提升variantif constexpr、折叠表达式、Concepts、Ranges
  • 面向未来:协程、std::formatstd::span

Modern C++的核心思想就一句话:把运行时的问题搬到编译期,把手动管理的事情交给RAII和标准库。 用好这些特性,bug少了,代码短了,性能还不差。

更多C++高性能开发实践,欢迎访问云栈社区。




上一篇:epoll 原理详解:从 select 到 epoll,Linux 高性能 I/O 模型演进之路
下一篇:JSON、Protobuf与MessagePack序列化实测:凭什么体积差4倍,速度差5倍?
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-6-5 04:22 , Processed in 0.645327 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表