C++中常用的十个特性(c++基本特性)
liuian 2025-07-28 18:13 51 浏览
C++ 语言自1983年发布以来,经历了多个版本的迭代。随着C++11、C++14、C++17、C++20等新版本的发布,在原有的基础上引入了大量现代化的特性,使得C++的编程体验更加强大和简洁。今天,C++不仅仅是一个强大的系统级编程语言,也逐渐成为了一门高效、灵活的开发工具,广泛应用于各类应用开发中。
大致是在10年前开始将Modern 用到项目中,那时编译器版本是gcc4.9.2,支持了C++11,也就是从那个时候开始接触Modern C++,开始发现了新世界的大门,后面为了使用新特性,着手升级了两次,经历了gcc11.2以及现在的gcc14.2版本。
本文主要聊聊项目中常用常用到的十个特性,这些特性能够帮助开发者提升代码的可读性、可维护性和执行效率。
PS:本文旨在简单介绍,而不是深入各个特性的用法或者实现细节~
希望能够借助本文,能够帮您更好的了解Modern C++。
基于范围的带初始化器的for循环
C++20在基于范围的for循环基础上,引入了新的功能初始化器。此功能允许我们在for循环头内声明变量,从而简化代码并缩小范围。
std::vector<int> vec = {1, 2, 3, 4, 5};
for (auto size = numbers.size(); auto&& num : vec) {
std::cout << num << " " << size;
}
这个特性特别适用于在循环中需要使用一个变量,但又希望将其作用域限制在循环内部,以防止在其他地方不小心使用该变量。
结构化绑定
C++17 引入的结构化绑定 (Structured bindings) 允许将元组、对 (pair) 和其他多元素对象解包为具名变量。这一特性极大地提升了代码的可读性。
#include <map>
#include <string>
#include <iostream>
int main() {
std::map<std::string, int> ages = {{"A", 30}, {"B", 25}};
for (auto&& [name, age] : ages) {
std::cout << name << " is " << age << " years old." << std::endl;
}
return 0;
}
结构化绑定在处理返回多个值的函数或遍历键值容器时特别有用。它使得解构和使用多个返回值变得更加简洁和直观。
inline变量
C++17 引入了inline变量,其语义与inline函数类似,只是它适用于变量。
最常见的是用于类内static变量的初始化:
class Test {
private:
inline static int value_ = 0; // 等同于类外执行 int Test::value = 0;
};
与inline function一样,inline variable也允许在多个编译单元对同一个变量进行定义,并且在链接时只保留其中的一份作为该变量的定义。当然,同时在多个源文件中定义同一个inline变量必须保证它们的定义都相同,否则和inline函数一样,你没办法保证链接器最终采用的是哪个定义。
inline variable除了支持类内静态成员初始化外,也支持头文件中定义全局变量,这样不会违反ODR规则。
std::optional
C++17 引入的 std::optional 提供了一种更安全、更具表现力的方式来处理可选值。它是使用哨兵值或指针来表示可选数据的一个很好的替代方案。
struct Result {
char c;
uint32_t pos;
};
std::optional<Result> GetFirstUpper(const std::string &str) {
std::optional<Result> res;
for (int i = 0; i < str.size(); ++i) {
if (std::isupper(str[i])) {
res.c = elem;
res.pos = i;
return res;
}
}
return std::nullopt;
}
std::optional 对于那些可能返回值也可能不返回值的函数特别有用,它在很多情况下消除了使用错误代码或异常的需求。
std::variant
std::variant,在 C++17 中引入,是一种类型安全的联合体,可以存储多个不同类型的值,但在任何时刻只能存储其中一种类型的值。与传统的 C 语言联合体不同,std::variant 提供了类型安全检查,避免了不小心访问错误类型的风险。
#include <variant>
#include <string>
#include <iostream>
int main() {
std::variant<int, float, std::string> data;
data = 10;
std::cout << std::get<int>(data) << std::endl;
data = "Hello";
std::cout << std::get<std::string>(data) << std::endl;
return 0;
}
std::variant 在需要以类型安全的方式处理多种类型的场景中特别有用,例如解析不同的数据格式或实现状态机。它可以帮助你在处理多个不同类型的数据时,避免类型错误,并且使得代码更加清晰和易于维护。
std::visit
std::visit 是 C++17 引入的一个函数模板,用于访问 std::variant 中存储的值。它提供了一种类型安全的方式来处理 std::variant 中的不同类型,并通过访问器函数或 lambda 表达式来操作存储的值。
#include <variant>
#include <string>
#include <iostream>
structVisitor {
void operator()(int i) { std::cout << "Integer: " << i << std::endl; }
void operator()(float f) { std::cout << "Float: " << f << std::endl; }
void operator()(const std::string& s) { std::cout << "String: " << s << std::endl; }
};
int main() {
std::variant<int, float, std::string> data = 3.14f;
std::visit(Visitor{}, data);
return0;
}
std::visit 在你需要根据 std::variant 中存储的类型执行不同操作时特别有用,它避免了类型检查和类型转换的繁琐过程。通过 std::visit,你可以直接根据 variant 中的实际类型来调用相应的操作,而无需手动进行类型识别或强制类型转换,从而保持代码的简洁和类型安全。
constexpr if
constexpr if 是 C++17 中引入的一个特性,它允许你在编译时根据条件选择性地包含或排除代码块。这使得模板编程更加灵活和高效。与常规的 if 语句不同,constexpr if 只有在条件为 true 时,相关代码才会被编译;如果条件为 false,编译器将会完全忽略对应的代码段,这样就避免了不必要的代码生成和运行时开销。
#include <type_traits>
#include <iostream>
template<typename T>
void PrintType(const T& value) {
if constexpr (std::is_integral_v<T>) {
std::cout << "Integral type: " << value << std::endl;
} elseifconstexpr (std::is_floating_point_v<T>) {
std::cout << "Floating-point type: " << value << std::endl;
} else {
std::cout << "Other type" << std::endl;
}
}
int main() {
PrintType(42);
PrintType(3.14);
PrintType("Hello");
return0;
}
constexpr if 使你能够编写更灵活的模板,并在许多情况下避免使用 SFINAE,从而使代码更加简洁和可维护。通过在编译时做出条件判断,constexpr if 可以根据类型或其他条件选择性地编译不同的代码,而不需要依赖复杂的类型特化或 SFINAE 技巧。这不仅提升了代码的可读性,还减少了模板编程中的冗余,简化了错误处理和调试过程。
default & delete
在 C++11 中,**默认函数(Defaulted Functions)和删除函数(Deleted Functions)**是两个重要的特性,用来控制类的构造、拷贝、赋值等操作的行为。它们使得代码更加安全和可维护,避免了不必要的默认行为或不合法的操作。
#include <iostream>
classObj {
public:
Obj() = default; // 默认构造函数
Obj(const Obj&) = default; // 默认拷贝构造函数
Obj& operator=(const Obj&) = default; // 默认赋值运算符
~Obj() = default; // 默认析构函数
};
int main() {
Obj obj1; // 使用默认构造函数
Obj obj2 = obj1; // 使用默认拷贝构造函数
obj1 = obj2; // 使用默认赋值运算符
return0;
}
nodiscard属性
[[nodiscard]] 属性是在 C++17 中引入的,它有助于防止开发者无意中忽略重要的返回值。当你将这个属性应用到一个函数或类型时,编译器会发出警告,提醒调用者不要忽视该函数的返回值。这个特性在返回错误码、状态信息或其他需要处理的结果时尤其有用。
#include <iostream>
[[nodiscard]] int GetArea(int width, int height) {
return width * height;
}
int main() {
GetArea(5, 10); // 这里会触发编译器警告,因为返回值被忽略了
int area = GetArea(5, 10); // 正确用法
std::cout << "Area: " << area << std::endl;
return 0;
}
std::string_view
std::string_view 是在 C++17 中引入的一个新特性,它提供了一个对字符串的非拥有性视图。与 const std::string& 不同,std::string_view 不会复制字符串数据,而是直接引用原始数据。它是一个高效的替代方案,尤其在传递字符串时,可以避免不必要的内存分配和拷贝。
#include <iostream>
#include <string_view>
void Print(std::string_view str) {
std::cout << "String: " << str << std::endl;
}
int main() {
std::string_view view = "Hello, World!";
Print(view);
return 0;
}
结语
这十个特性,在项目中用的算是很多了,当然了,还有lambda、auto等,在此就不再一一赘述了,通过此文,希望能帮助到您~
相关推荐
- 赶紧收藏!编程python基础知识,本文给你全部整理好了
-
想一起学习编程Python的同学,趁我粉丝少,可以留言、私信领编程资料~Python基础入门既然学习Python,那么至少得了解下这门编程语言,知道Python代码执行过程吧。Python的历...
- 创建绩效改进计划 (PIP) 的6个步骤
-
每个经理都必须与未能达到期望的员工抗衡,也许他们的表现下降了,他们被分配了新的任务并且无法处理它们,或者他们处理了自己的任务,但他们的行为对他人造成了破坏。许多公司转向警告系统,然后在这些情况下终止。...
- PI3K/AKT信号通路全解析:核心分子、上游激活与下游效应分子
-
PI3K/AKT/mTOR(PAM)信号通路是真核细胞中高度保守的信号转导网络,作用于促进细胞存活、生长和细胞周期进程。PAM轴上生长因子向转录因子的信号传导受到与其他多条信号通路的多重交叉相互作用的...
- 互联网公司要求签PIP,裁员连N+1都没了?
-
2021年刚画上句号,令无数互联网公司从业者闻风丧胆的绩效公布时间就到了,脉脉上已然炸了锅。阿里3.25、腾讯二星、百度四挡、美团绩效C,虽然名称五花八门,实际上都代表了差绩效。拿到差绩效,非但不能晋...
- Python自动化办公应用学习笔记3—— pip工具安装
-
3.1pip工具安装最常用且最高效的Python第三方库安装方式是采用pip工具安装。pip是Python包管理工具,提供了对Python包的查找、下载、安装、卸载的功能。pip是Python官方提...
- 单片机都是相通的_单片机是串行还是并行
-
作为一个七年的从业者,单片机对于我个人而言它是一种可编程的器件,现在长见到的电子产品中几乎都有单片机的身影,它们是以单片机为核心,根据不同的功能需求,搭建不同的电路,从8位的单片机到32位的单片机,甚...
- STM32F0单片机快速入门八 聊聊 Coolie DMA
-
1.苦力DMA世上本没有路,走的人多了,便成了路。世上本没有DMA,需要搬运的数据多了,便有了DMA。大多数同学应该没有在项目中用过这个东西,因为一般情况下也真不需要这个东西。在早期的单片机中...
- 放弃51单片机,直接学习STM32开发可能会面临的问题
-
学习51单片机并非仅仅是为了学习51本身,而是通过它学习一种方法,即如何仅仅依靠Datasheet和例程来学习一种新的芯片。51单片机相对较简单,是这个过程中最容易上手的选择,而AVR单片机则更为复杂...
- STM32串口通信基本原理_stm32串口原理图
-
通信接口背景知识设备之间通信的方式一般情况下,设备之间的通信方式可以分成并行通信和串行通信两种。并行与串行通信的区别如下表所示。串行通信的分类1、按照数据传送方向,分为:单工:数据传输只支持数据在一个...
- 单片机的程序有多大?_单片机的程序有多大内存
-
之前一直很奇怪一个问题,每次写好单片机程序之后,用烧录软件进行烧录时,能看到烧录文件也就是hex的文件大小:我用的单片机芯片是STM32F103C8T6,程序储存器(flash)只有64K。从...
- 解析STM32单片机定时器编码器模式及其应用场景
-
本文将对STM32单片机定时器编码器模式进行详细解析,包括介绍不同的编码器模式、各自的优缺点以及相同点和不同点的应用场景。通过阅读本文,读者将对STM32单片机定时器编码器模式有全面的了解。一、引言...
- 两STM32单片机串口通讯实验_两个32单片机间串口通信
-
一、实验思路连接两个STM32单片机的串口引脚,单片机A进行发送,单片机B进行接收。单片机B根据接收到单片机A的指令来点亮或熄灭板载LED灯,通过实验现象来验证是否通讯成功。二、实验器材两套STM32...
- 基于单片机的智能考勤机设计_基于51单片机的指纹考勤机
-
一、设计背景随着科技水平的不断发展,在这么一个信息化的时代,智能化信息处理已是提高效率、规范管理和客观审查的最有效途径。近几年来,国内很多公司都在加强对企业人员的管理,考勤作为企业的基础管理,是公司...
- STM32单片机详细教学(二):STM32系列单片机的介绍
-
大家好,今天给大家介绍STM32系列单片机,文章末尾附有本毕业设计的论文和源码的获取方式,可进群免费领取。前言STM32系列芯片是为要求高性能、低成本、低功耗的嵌入式应用设计的ARMCortexM...
- STM32单片机的 Hard-Fault 硬件错误问题追踪与分析
-
有过单片机开发经验的人应该都会遇到过硬件错误(Hard-Fault)的问题,对于这样的问题,有些问题比较容易查找,有些就查找起来很麻烦,甚至可能很久都找不到问题到底是出在哪里。特别是有时候出现一次,后...
- 一周热门
-
-
【验证码逆向专栏】vaptcha 手势验证码逆向分析
-
Python实现人事自动打卡,再也不会被批评
-
Psutil + Flask + Pyecharts + Bootstrap 开发动态可视化系统监控
-
一个解决支持HTML/CSS/JS网页转PDF(高质量)的终极解决方案
-
再见Swagger UI 国人开源了一款超好用的 API 文档生成框架,真香
-
网页转成pdf文件的经验分享 网页转成pdf文件的经验分享怎么弄
-
C++ std::vector 简介
-
飞牛OS入门安装遇到问题,如何解决?
-
系统C盘清理:微信PC端文件清理,扩大C盘可用空间步骤
-
10款高性能NAS丨双十一必看,轻松搞定虚拟机、Docker、软路由
-
- 最近发表
- 标签列表
-
- python判断字典是否为空 (50)
- crontab每周一执行 (48)
- aes和des区别 (43)
- bash脚本和shell脚本的区别 (35)
- canvas库 (33)
- dataframe筛选满足条件的行 (35)
- gitlab日志 (33)
- lua xpcall (36)
- blob转json (33)
- python判断是否在列表中 (34)
- python html转pdf (36)
- 安装指定版本npm (37)
- idea搜索jar包内容 (33)
- css鼠标悬停出现隐藏的文字 (34)
- linux nacos启动命令 (33)
- gitlab 日志 (36)
- adb pull (37)
- python判断元素在不在列表里 (34)
- python 字典删除元素 (34)
- vscode切换git分支 (35)
- python bytes转16进制 (35)
- grep前后几行 (34)
- hashmap转list (35)
- c++ 字符串查找 (35)
- mysql刷新权限 (34)