中新网重庆9月6日电 (记者 钟旖 记者6日从重庆市科技局获悉,近年来,鲁渝两地科技部门以项目为“点”、人才为“线”、机构为“面”、平台为“体”,全方...
2025-09-07 0
原作者:Linux教程,原文「链接」:https://mp.weixin.qq.com/s/oFbiFlqiwgVcJIMMvTelEA
很多刚刚进入 C++ 领域的朋友,最初是从 C 语言转过来的。因此在编写 C++ 代码时,往往会不自觉地延续 C 的语法风格和编程思维。
然而,随着 C++ 的不断演进,特别是从 C++11 开始,这门语言在语法特性和编程范式上发生了显著变化。许多新特性不仅提升了代码的安全性与可读性,也极大地增强了开发效率。
最近也有朋友和我聊到,现在的 C++ 看起来越来越“陌生”,有些语法甚至已经看不懂了。这也说明了了解现代 C++ 的重要性。
因此,对于刚接触 C++ 的新手来说,非常有必要对现代 C++ 的发展有一个整体的认识。今天我们就从现代化 C++ 的起点 —— C++11 标准 开始,一起聊聊它引入了哪些实用且重要的新特性。
跨越十年的C++演进系列,分为5篇,本文为第一篇,后续会持续更新C++14、C++17、C++20、C++23~
C++11(也被称为 C++0x)是 C++ 编程语言的一个重大更新版本,于 2011 年正式发布。它是对 C++98/03 的一次全面升级,标志着现代 C++ 的开端。
该标准在原有基础上引入了约 140 个新特性,并修正了大约 600 个已知的语言缺陷。这些改进使得 C++ 更加简洁、安全、高效,同时也为后续的标准(如 C++14、C++17、C++20 等)奠定了坚实的基础。
接下来的内容中,将详细介绍 C++11 中一些具有代表性的新特性,并通过实际代码示例帮助你更好地理解和使用这些特性。
为了解决 C++ 中多种初始化语法带来的歧义和复杂性,C++11 引入了统一初始化(Uniform Initialization)机制,使用 花括号 {} 作为通用的初始化语法。
这种初始化方式具有以下优势:
#include <iostream>class Foo {public: Foo(int) { std::cout << "Foo constructed with int\n"; }private: Foo(const Foo&); // 私有拷贝构造函数,禁止拷贝};int main() { Foo a1(123); // 直接调用构造函数,合法 // Foo a2 = 123; // 错误!会尝试隐式调用拷贝构造函数,但该函数是私有的 Foo a3 = {123}; // 合法!列表初始化,直接调用构造函数 Foo a4{123}; // 合法!另一种写法,等效于上一行 int a5 = {3}; // 基本类型初始化,合法 int a6{3}; // 另一种写法,同样合法 return 0;}
Foo constructed with intFoo constructed with intFoo constructed with int
在 C++ 程序开发中,为了提升代码的安全性和可读性,通常建议在定义变量时立即进行初始化。特别是在使用指针时,如果指向不明确,容易产生野指针,从而引发程序异常。
C++11 引入了关键字 auto,它允许编译器根据变量的初始化表达式自动推导其类型。这一特性不仅简化了复杂类型的书写,也提升了代码的可维护性。
示例代码:
#include <iostream>#include <vector>int main() { // 定义一个二维向量 std::vector<std::vector<int>> v = {{1, 2, 3}, {4, 5, 6}}; // 传统方式:手动指定迭代器类型 std::vector<std::vector<int>>::iterator it = v.begin(); // 使用 auto:让编译器自动推导类型 auto it_auto = v.begin(); // 更简洁且易读 // 遍历第一个子向量并输出 for (auto inner : *it_auto) { std::cout << inner << " "; // 输出: 1 2 3 } std::cout << std::endl; // 基本数据类型也能自动推导 auto x = 5; // 推导为 int auto y = 3.14; // 推导为 double return 0;}
1 2 3
在 C++11 中,引入了新的关键字 nullptr,用于替代旧版 C++(C++98/03)中的宏定义 NULL。
nullptr 的类型是 std::nullptr_t,它是一个专门表示空指针的字面量,能更明确地表达“空指针”的语义,避免了一些潜在的类型歧义问题。
#include <iostream>// 函数重载示例void foo(int i) { std::cout << "foo_int" << std::endl;}void foo(char* pc) { std::cout << "foo_char*" << std::endl;}int main() { // foo(NULL); // 错误!编译器无法确定调用哪一个函数,存在歧义 foo(nullptr); // 正确!明确调用 foo(char*),因为 nullptr 是指针类型 return 0;}
foo_char*
在 C++11 中引入了 enum class,也称为强类型枚举(scoped enumeration),它解决了传统枚举(enum)存在的一些问题,如命名冲突和隐式类型转换。
#include <iostream>// 定义两个强类型枚举enum class Apple { green, red };enum class Orange { big, small };int main() { // 使用作用域访问枚举值 Apple a = Apple::green; Orange o = Orange::big; // 尝试比较不同类型的枚举值 if (a == o) { std::cout << "绿苹果 = 大橘子" << std::endl; } else { std::cout << "绿苹果 != 大橘子" << std::endl; } return 0;}
如果你确实需要将 enum class 值转换为整数,可以使用 static_cast:
int appleValue = static_cast<int>(Apple::green); // 正确:appleValue = 0
Lambda 表达式是C++11最重要也是最常用的特性之一。它允许我们在代码中就地定义匿名函数对象,极大提升了代码的简洁性和可读性。
特别是在使用标准库算法时,lambda 表达式常常能避免定义额外的函数或函数对象,使逻辑更加紧凑、直观。
#include <iostream>#include <vector>#include <algorithm>int main() { std::vector<int> numbers = {1, 2, 3, 4, 5}; // 使用 lambda 表达式作为比较函数进行排序(升序) std::sort(numbers.begin(), numbers.end(), [](int a, int b) { return a < b; }); // 输出排序后的结果 for (auto number : numbers) { std::cout << number << " "; } std::cout << std::endl; return 0;}
在使用 Lambda 表达式时,捕获列表(Capture List) 决定了 Lambda 是否以及如何访问其定义所在作用域中的变量。
默认情况下,Lambda 表达式无法修改通过值捕获的外部变量,因为这些变量是以副本的形式被捕获的,并且在 Lambda 函数体内被视为 const 类型。
捕获方式 | 含义 |
[] | 不捕获任何外部变量 |
[x] | 以值的方式捕获变量 x |
[&x] | 以引用的方式捕获变量 x |
[=] | 以值的方式捕获所有使用的外部变量(隐式捕获) |
[&] | 以引用的方式捕获所有使用的外部变量(隐式捕获) |
[this] | 捕获当前类对象的 this 指针,允许访问成员变量和函数 |
在 C++ 中,没有自动垃圾回收机制,因此开发者必须手动管理动态分配的内存。如果忘记释放不再使用的内存,就可能导致内存泄漏;而如果访问了已经被释放的内存,则可能引发悬空指针问题。
为了解决这些问题,C++11 引入了智能指针(Smart Pointers),作为资源管理的重要工具。它们通过 RAII(Resource Acquisition Is Initialization)机制,确保内存资源在对象生命周期结束时被自动释放,从而大大提升代码的安全性和可维护性。
#include <iostream>#include <memory>int main() { // 创建 unique_ptr 管理一个堆上的整数 std::unique_ptr<int> ptr(new int(42)); std::cout << "ptr value: " << *ptr << std::endl; // 无法复制 // std::unique_ptr<int> ptr2 = ptr; // 编译错误 // 可以转移所有权 std::unique_ptr<int> ptr2 = std::move(ptr); if (ptr == nullptr) { std::cout << "ptr is now null" << std::endl; } return 0;}
#include <iostream>#include <memory>int main() { // 创建 shared_ptr 管理一个堆上的整数 std::shared_ptr<int> ptr1 = std::make_shared<int>(100); { std::shared_ptr<int> ptr2 = ptr1; // 共享所有权 std::cout << "Use count inside block: " << ptr1.use_count() << std::endl; // 输出 2 } // ptr2 离开作用域,引用计数减1 std::cout << "Use count outside block: " << ptr1.use_count() << std::endl; // 输出 1 return 0;}
#include <iostream>#include <memory>struct A;struct B;struct A { std::shared_ptr<B> b_ptr; ~A() { std::cout << "A destroyed\n"; }};struct B { std::weak_ptr<A> a_ptr; // 使用 weak_ptr 避免循环引用 ~B() { std::cout << "B destroyed\n"; }};int main() { std::shared_ptr<A> a = std::make_shared<A>(); std::shared_ptr<B> b = std::make_shared<B>(); a->b_ptr = b; b->a_ptr = a; return 0;} // a 和 b 被正确析构,不会有内存泄漏
C++11 标准首次在语言层面引入了对多线程并发编程的支持,标志着 C++ 正式迈入现代并发编程时代。它不仅定义了一个新的内存模型(memory model),还提供了标准库中的多线程工具,如:
这些特性使得开发者可以在不依赖平台相关 API 的前提下,编写可移植、安全且高效的并发程序。
#include <iostream>#include <thread>#include <mutex>#include <condition_variable>std::mutex mtx; // 互斥锁std::condition_variable cv; // 条件变量bool ready = false; // 共享状态标志// 线程函数:等待“就绪”信号void print_id(int id) { std::unique_lock<std::mutex> lock(mtx); while (!ready) { cv.wait(lock); // 阻塞等待通知 } // 当 ready == true 时继续执行 std::cout << "Thread " << id << '\n';}// 主线程调用此函数,触发所有线程继续执行void go() { std::unique_lock<std::mutex> lock(mtx); ready = true; cv.notify_all(); // 唤醒所有等待的线程}int main() { const int thread_count = 10; std::thread threads[thread_count]; // 创建并启动多个线程 for (int i = 0; i < thread_count; ++i) { threads[i] = std::thread(print_id, i); } std::cout << "10 threads ready to race...\n"; go(); // 触发所有线程开始执行 // 等待所有线程完成 for (auto& th : threads) { th.join(); } return 0;}
C++11 引入了 右值引用(Rvalue Reference),使用 && 声明,专门用于绑定到临时对象(右值),从而实现对资源的高效转移。
右值引用是现代 C++ 实现移动语义(Move Semantics)和完美转发(Perfect Forwarding)的基础,极大地提升了程序性能并增强了泛型编程能力。
#include <iostream>int main() { int x = 10; // x 是左值 int&& rref = 20; // 20 是右值,rref 是右值引用 std::cout << "右值引用的值: " << rref << std::endl; return 0;}
在这个例子中:
右值引用的核心作用
传统的拷贝构造函数和赋值运算符会进行深拷贝,对于包含动态资源(如堆内存、文件句柄等)的大对象来说,代价高昂。
而 C++11 引入的 移动构造函数 和 移动赋值运算符 利用右值引用,允许我们直接“偷走”临时对象的资源,而不是复制它。
#include <iostream>#include <vector>class MyVector {public: std::vector<int>* data; // 构造函数 MyVector() : data(new std::vector<int>({1, 2, 3})) { std::cout << "构造\n"; } // 拷贝构造函数(深拷贝) MyVector(const MyVector& other) : data(new std::vector<int>(*other.data)) { std::cout << "拷贝构造\n"; } // 移动构造函数(资源转移) MyVector(MyVector&& other) noexcept : data(other.data) { other.data = nullptr; // 把原对象置空,防止多次释放 std::cout << "移动构造\n"; } // 析构函数 ~MyVector() { if (data) delete data; std::cout << "析构\n"; }};MyVector createTemp() { MyVector v; return v; // 返回临时对象,将调用移动构造函数}int main() { MyVector a = createTemp(); // 这里将调用移动构造函数(而非拷贝构造) return 0;}
可以看到,返回临时对象时并没有发生深拷贝,而是通过移动语义完成了资源的转移。
右值引用结合模板类型推导和 std::forward,可以实现完美转发,即在函数模板中将参数以原始值类别(左值/右值)传递给另一个函数。
这在编写通用库函数(如 std::make_shared、std::vector::emplace_back 等)时非常有用。
#include <iostream>#include <utility> // std::forwardtemplate<typename T>void wrapper(T&& arg) { call(std::forward<T>(arg)); // 将 arg 原样转发给 call()}
9.正则表达式
正则表达式是一种强大的文本处理工具,它通过特定的语法规则构建匹配模式,主要用于实现以下核心功能:
1.模式验证:检测字符串中是否存在符合特定规则的子串
2.文本替换:查找并替换符合模式的字符串片段
3.数据提取:从文本中捕获符合特定格式的内容片段
以下是 C++11 中正则表达式库的一些关键组件:
示例代码:
#include <iostream>#include <string>#include <regex>int main() { std::string s("Example string for regex matching."); std::regex e("regex"); // 正则表达式字面量 // 使用 regex_search 查找匹配的子串 if (std::regex_search(s, e)) { std::cout << "String contains 'regex'.\n"; } else { std::cout << "String does not contain 'regex'.\n"; } // 使用 regex_replace 替换匹配的子串 std::string replaced = std::regex_replace(s, e, "pattern"); std::cout << "Replaced string: " << replaced << std::endl; // 使用 smatch 存储匹配结果 std::smatch match; if (std::regex_search(s, match, e)) { std::cout << "Match found: " << match.str() << std::endl; } return 0;}
C++11的新特性还包括static_assert、override/final、constexpr等,这些特性共同提升了C++语言的表达能力和开发效率。
相关文章
中新网重庆9月6日电 (记者 钟旖 记者6日从重庆市科技局获悉,近年来,鲁渝两地科技部门以项目为“点”、人才为“线”、机构为“面”、平台为“体”,全方...
2025-09-07 0
您好:这款游戏是可以开挂的,软件加微信【添加图中微信】确实是有挂的,很多玩家在这款游戏中打牌都会发现很多用户的牌特别好,总是好牌,而且好像能看到其他人...
2025-09-07 0
您好:这款游戏是可以开挂的,软件加微信【添加图中微信】确实是有挂的,很多玩家在这款游戏中打牌都会发现很多用户的牌特别好,总是好牌,而且好像能看到其他人...
2025-09-07 0
本篇文章给大家谈谈手机麻将怎么买到挂,以及手机麻将挂到底在哪里购买对应的知识点,希望对各位有所帮助,不要忘了收藏本站喔。 麻将怎样刮花 1、一般的使用...
2025-09-07 0
现在人们打棋牌麻将谁不想赢?手机微乐麻将必赢神器但是手机棋牌麻将是这么好赢的吗?在手机上打棋牌麻将想赢,不仅需要运气,也需要技巧。掌握的棋牌麻将技巧就...
2025-09-07 0
大炮一响黄金万两,一场补贴混战,让美团感觉被掏空。8月27日,美团2025第二季度财报出炉,市场早已预测这家巨头会为补贴大战买单,但没想到这个“单”竟...
2025-09-07 0
北京时间2025年9月7日00时34分,我国在太原卫星发射中心使用长征六号改运载火箭,成功将遥感四十号03组卫星发射升空,卫星顺利进入预定轨道,发射任...
2025-09-07 0
发表评论