【学习笔记】C++代码规范整理

一、匿名空间namespace

匿名命名空间(Anonymous Namespace)是一种特殊的命名空间声明方式,其作用是将声明的成员限定在当前编译单元(源文件)内可见,类似于使用 static 关键字修饰全局变量 / 函数的效果。

特性匿名命名空间static 修饰全局符号
作用域当前编译单元(源文件)当前编译单元
链接属性内部链接(Internal Linkage)内部链接
可修饰类型变量、函数、类、结构体等所有实体仅变量、函数
语法灵活性可嵌套在其他命名空间中不可嵌套
外部访问规则在匿名命名空间所属的源文件内,可直接调用成员函数外部通过命名空间名+“::”访问通过中间接口封装

外部访问namespace内部接口:

// test.h(头文件,声明具名命名空间)
namespace MyNamespace {void sharedFunc(); // 声明为具名命名空间成员
}// test.cpp(源文件,定义具名命名空间函数)
#include "test.h"
namespace MyNamespace {void sharedFunc() { // 具名命名空间,可跨文件访问std::cout << "Shared function called.\n";}
}// other.cpp(其他源文件,包含头文件并调用)
#include "test.h"
int main() {MyNamespace::sharedFunc(); // 合法,通过命名空间名访问return 0;
}

访问static关键字定义的接口函数:通过中间接口封装

// file1.c
static void privateFunc() { /* ... */ } // 内部函数void callPrivateFunc() { // 公有接口privateFunc(); // 内部调用
}// file2.c
extern void callPrivateFunc(); // 声明公有接口
int main() {callPrivateFunc(); // 通过公有接口间接调用return 0;
}

二、结构体对齐

在结构体中合理安排数据成员的布局可以有效减少内存占用。

核心原则:

​ 1. 先放占用空间大的成员,再放小的成员:减少成员之间的填充字节。

​ 2. 相同大小的成员分组排列:避免小成员插入大成员之间导致的零散填充。

内存对齐规则(以常见编译器为例):

​ ● 每个成员的起始地址必须是其自身大小的整数倍(如 int 占 4 字节,起始地址需是 4 的倍数)。

​ ● 结构体的总大小必须是最大成员大小的整数倍。

struct BadLayout {char a;       // 1字节,起始地址对齐0,占用1字节         地址:0double b;  // 8字节,起始地址需是8的倍数 → 填充7字节,占用8字节   地址:8-15int c;          // 4字节,起始地址需是4的倍数(当前地址是16),占用4字节   地址:16-19
};
// 总大小:1(a)+7(填充)+8(b)+4(c) = 20 → 按最大成员8字节对齐,最终大小24字节
struct GoodLayout {double b;  // 8字节,起始地址对齐8,占用8字节     地址:0-7int c;          // 4字节,起始地址对齐4(当前地址8是4的倍数),占用4字节     地址:8-11char a;       // 1字节,起始地址对齐1(当前地址12),占用1字节   地址:12
};
// 总大小:8+4+1 = 13 → 按最大成员8字节对齐,最终大小16字节(节省8字节)

三、#pragma once

#pragma once 是一种预处理指令,用于确保头文件在编译过程中只被包含一次,从而防止因重复包含导致的编译错误,如 “重复定义”(multiple definition)问题。

四、虚析构函数

一个析构函数不为virtual 的类,就是一个不愿被继承的类。

当基类析构函数不是虚函数时,要是通过基类指针删除派生类对象,系统只会调用基类的析构函数,而不会调用派生类的析构函数。这就可能使派生类特有的资源(像动态分配的内存、文件句柄、网络连接等)无法被释放,进而造成内存泄漏。

五、const

const 关键字用于声明一个对象或变量是不可变的,即其值在初始化后不能被修改。

类的成员函数后面加const,表明这个函数不会对这个类对象的数据成员作任何改变。

六、尽量使用栈内存

程序运行中创建对象时主要在两个地方,栈和堆。

在栈中创建对象(或数组)是编译期确定的,因此开销为零。

在堆中申请内存是运行期行为,申请、释放都有开销,并且存在内存碎片可能。

1.内部碎片的产生:

因为所有的内存分配必须起始于可被 4、8 或 16 整除(视处理器体系结构而定)的地址或者因为MMU的分页机制的限制,决定内存分配算法仅能把预定大小的内存块分配给客户。假设当某个客户请求一个43字节的内存块时,因为没有适合大小的内存,所以它可能会获得 44字节、48字节等稍大一点的字节,因此由所需大小四舍五入而产生的多余空间就叫内部碎片。

2.外部碎片的产生:

频繁的分配与回收物理页面会导致大量的、连续且小的页面块夹杂在已分配的页面中间,就会产生外部碎片。

假设有一块一共有100个单位的连续空闲内存空间,范围是099。如果你从中申请一块内存,如10个单位,那么申请出来的内存块就为09区间。这时候你继续申请一块内存,比如说5个单位大,第二块得到的内存块就应该为1014区间。如果你把第一块内存块释放,然后再申请一块大于10个单位的内存块,比如说20个单位。因为刚被释放的内存块不能满足新的请求,所以只能从15开始分配出20个单位的内存块。现在整个内存空间的状态是09空闲,1014被占用,1524被占用,2599空闲。其中09就是一个内存碎片了。如果1014一直被占用,而以后申请的空间都大于10个单位,那么09就永远用不上了,变成外部碎片。

七、减少宏的使用

因为宏只是简单的文本替换,缺乏类型检查,因此不推荐使用。

除非绝对必要(如条件编译),完全避免使用宏定义常量,统一采用 const(运行时常量)或 constexpr(编译时常量)

// 宏方式(不推荐)
#define BUFFER_SIZE 1024
#define APP_VERSION "1.0.0.12"       //  无类型// constexpr方式(推荐)
constexpr int BUFFER_SIZE = 1024;
constexpr const char* APP_VERSION = "10.0.0.22";  // 必须加 const或者:
#include <string_view>
constexpr std::string_view APP_VERSION = "10.0.0.22";  // C++17+

所以一般正常的宏定义就可以直接用constexpr代替了。const就用作常量使用即可。

关键字常量性质初始化时机典型用途
const运行时常量运行时初始化值在运行时确定(如配置文件读取)
constexpr编译时常量编译时初始化值必须在编译期确定(如数组长度、模板参数

八、nullptr 代替宏NULL

C++11 之前,宏NULL 代表空指针,但是被定义为0,存在类型歧义。采用C++11 新增的关键字nullptr 代替NULL。

九、固定数组使用std::array 容器

C++ 11 新增了std::array 容器,用来存放固定大小的数组,访问元素时具有越界检查功能。

std::array<int, 5> arr = {1,2,3,4,5};
// arr[10] = 0;  // 原生数组:未定义行为(可能崩溃)
arr.at(10) = 0;  // 抛出 std::out_of_range 异常,安全捕获错误

原生数组需通过 sizeof(arr)/sizeof(arr[0]) 计算长度,而 std::array 直接提供 size() 方法:

std::array<int, 5> arr;
std::cout << arr.size();  // 直接获取,编译期常量,安全高效

十、动态数组使用std::vector

使用std::vector 代替new[],利用new[] 动态申请内存,要用delete[] 释放,容易发生内存泄漏。std::vector离开作用域自动释放。并且返回的指针本身并没有包含size 信息,访问时不会进行越界检查。std::vector 可以自动管理内存,并且访问内存时可以进行边界检查。

std::vector<int> vec(5);
// vec[10] = 0;  // 未定义行为(可能崩溃)
vec.at(10) = 0;  // 抛出 std::out_of_range 异常,安全捕获错误

十一、unordered_map 代替std::map

C++ 11 新增了unordered_map,采用hash table 的方式实现,插入和查找数据都是O(1)速度。std::map采用的好像是红黑树。

十二、using代替typedef

使用using 代替typedef 定义类型别名。

typedef int IntType;           // 定义类型别名
typedef void (*FuncPtr)(int);  // 定义函数指针别名using IntType = int;           // 等价于 typedef
using FuncPtr = void (*)(int); // 等价于 typedef// 模板别名(typedef 无法实现)
template<typename T>
using Vec = std::vector<T>;
// 模板别名(using 专属)
template<typename T>
using MapString = std::map<std::string, T>;MapString<int> age_map;  // 等价于 std::map<std::string, int>// 若用 typedef 实现相同功能,需借助模板类+typedef
template<typename T>
struct MapString {typedef std::map<std::string, T> type;
};MapString<int>::type age_map;  // 语法冗余

十三、enum class代替enum

在 C++ 中,enum class(强类型枚举) 是 C++11 引入的特性,用于替代传统的 enum(普通枚举)。相比普通枚举,enum class 提供了更严格的类型安全和作用域控制,解决了传统枚举的诸多缺陷。

作用域控制(避免命名冲突)

// 普通枚举(命名冲突)
enum Color { RED, GREEN, BLUE };
enum TrafficLight { RED, YELLOW, GREEN };  // 错误:重复定义 RED、GREEN// 强类型枚举(无冲突)
enum class Color { RED, GREEN, BLUE };
enum class TrafficLight { RED, YELLOW, GREEN };Color c = Color::RED;  // 必须通过枚举类型访问
TrafficLight t = TrafficLight::RED;  // 无命名冲突

类型安全(禁止隐式转换)

enum OldEnum { A, B };
enum class NewEnum { A, B };void func(int x) { /* ... */ }func(A);  // 普通枚举:合法(隐式转换为 int)
// func(NewEnum::A);  // 错误:强类型枚举不可隐式转换
func(static_cast<int>(NewEnum::A));  // 必须显式转换

显式底层类型

// 指定底层类型为 uint8_t(节省内存)
enum class Status : uint8_t {OK = 0,ERROR = 1,PENDING = 2
};// 普通枚举无法指定底层类型,可能浪费内存
enum OldStatus {OK,  // 通常为 int(4字节)ERROR,PENDING
};

十四、重写明确使用override

在子类中重写父类的虚函数时,虚函数的签名(函数名+参数)必须与父类中的完全一样。

如果稍有不同,就会被编译器当作重载(overload)。

class Shape {
public:virtual double area() const = 0;
};class Circle : public Shape {
public:double area() const override { return 3.14 * r * r; }  // 明确重写
private:double r;
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/909830.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/909830.shtml
英文地址,请注明出处:http://en.pswp.cn/news/909830.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数学复习笔记 29 不定积分

前言 复习的时候时常复习一下以前复习的高数&#xff0c;温故而知新&#xff0c;可以为师矣。现实生活中有非常多让我难受的事情&#xff0c;贤者模式的时候也会对一些自己的行为嗤之以鼻&#xff0c;复习考研对其他三门科目都没有学出来正反馈&#xff0c;只能从数学这儿找一…

微软因安全漏洞禁用黑暗环境下的Windows Hello面部识别功能

面部识别技术在各种场景中的应用日益广泛&#xff0c;但身份欺诈事件仍时有发生&#xff0c;这表明即使面部识别也并非万无一失。在某些情况下&#xff0c;这些系统可能被攻击者利用。 漏洞发现与应对措施 近期&#xff0c;微软在Windows 10和11系统中禁用了黑暗环境下使用Wi…

产品经理课程(十五)实战点评(3)

&#xff08;一&#xff09;复习 需求文档是产品的说明书 需求文档包含&#xff1a;修订记录、背景、主要流程、详细功能逻辑、数据上报&#xff0c;发布策略 bug也是需求文档的一部分 &#xff08;二&#xff09;案例讲解 案例一&#xff1a; 一个版本里面的4-5个功能点就比…

大模型量化与剪枝

大模型量化&#xff0c;剪枝 量化有助于减少显存使用并加速推理 GPTQ 等后训练量化方法(Post Training Quantization)是一种在训练后对预训练模型进行量化的方法。 ### model model_name_or_path: meta-llama/Meta-Llama-3-8B-Instruct template: llama3### export export_di…

Oracle 数据库数据操作:精通 INSERT, UPDATE, DELETE

作者&#xff1a;IvanCodes 日期&#xff1a;2025年6月18日 专栏&#xff1a;Oracle教程 在 Oracle 数据库中&#xff0c;对表内数据进行增加、修改和删除操作是通过数据操作语言 (DML - Data Manipulation Language) 来完成的。核心的DML语句包括 INSERT (插入新数据), UPDATE…

推荐使用的Unity插件(InputSystem)

本文将提供更简洁的步骤和常见问题解决。 一、极简入门步骤&#xff1a; 安装&#xff1a;Package Manager中安装Input System&#xff08;确保Unity版本在2019.4&#xff09; 创建Input Actions&#xff1a; 在Project窗口右键 -> Create -> Input Actions 双击打开…

清理 Docker 容器日志文件方法

操作总结与问题解析 一、操作目的与背景 用户旨在清理 Docker 容器日志文件以释放服务器存储空间,主要通过以下命令组合完成操作: 查看容器日志空间占用清空指定容器的日志文件验证容器运行状态与日志清理效果二、关键命令与输出解析 1. 查看 Docker 容器日志空间占用 du…

图片压缩工具 | 按指定高度垂直切割图片

OPEN-IMAGE-TINY&#xff0c;一个基于 Electron VUE3 的图片压缩工具&#xff0c;项目开源地址&#xff1a;https://github.com/0604hx/open-image-tiny ℹ️ 需求描述 在上一篇文章一段代码利用 snapdom 将 CSDN 博文转化为长图片&#xff08;PNG/JPG/PDF&#xff09;中&…

山东大学软件学院创新项目实训开发日志——第十七周(二)

目录 1.优化前端日历页面显示&#xff0c;增加鼠标悬停显示当日会议基本信息的效果。 2.优化会议计划生成与会议PPT生成功能&#xff0c;使得能够加载多页docx文件与PDF文件 3.优化了会议PPT生成功能&#xff0c;使得可以上传多个文件并根据多个文件生成会议PPT 4.修改了识…

Ubuntu 使用kubeadm部署k8s系统组件反复重启的问题

系统&#xff1a;Ubuntu 24.04 LTS 问题现象&#xff1a;kubeadm init 后系统组件proxy、apiserver、etcd等频繁挂掉重启 问题原因&#xff1a;cgroup配置问题 解决方法&#xff1a; 编辑系统cgroup默认配置文件 sudo nano /etc/default/grub 将GRUB_CMDLINE_LINUX_DEFAU…

Oracle获取执行计划之EXPLAIN PLAN 技术详解

#Oracle #执行计划 #EXPLAIN PLAN 一、引言 在Oracle数据库性能优化中&#xff0c;​执行计划&#xff08;Execution Plan&#xff09;​是理解SQL语句如何被数据库处理的关键工具。EXPLAIN PLAN是Oracle提供的一种静态分析SQL执行路径的方法&#xff0c;它通过生成逻辑执行…

【论文阅读】Qwen2.5-VL Technical Report

Arxiv:https://arxiv.org/abs/2502.13923 Source code:https://github.com/QwenLM/Qwen2.5-VL Author’s Institution&#xff1a;Alibaba 背景 多模态大模型 多模态大模型MultiModal Large Language Models (MM-LLMs) 的发展可以通过一篇综述了解&#xff1a;MM-LLMs: Re…

vue中computed和watch区别

在 Vue 中&#xff0c;computed 和 watch 都是用来响应式地处理数据变化的工具&#xff0c;但它们的用途和行为有明显区别。 &#x1f50d; 一句话总结 computed&#xff1a;用于声明式计算属性&#xff0c;有缓存。 watch&#xff1a;用于监听响应式数据的变化并执行副作用逻…

大语言模型:提示词决定自我反思效果: “检查回答是否错误” “验证回答是否正确”

大语言模型(LLMs)自我反思的结果很容易受提示词构造的影响 大型语言模型(LLMs)展现出令人印象深刻的零样本和少样本推理能力。有人提出,这些能力可以通过自我反思来提升,即让大型语言模型反思自己的输出,以识别和纠正初始回答中的错误。然而,尽管有一些证据表明自我反…

【iReport】实际开发中,解决iReport中打印图片不显示问题

ireport 中增加图片&#xff0c;添加上属性&#xff0c;但是运行时报错如下&#xff0c;是属性logoPath没有声明到map中 1. Parameter not found : logoPath net.sf.jasperreports.engine.design.JRValidationException: Report design not valid : 1. Parameter not fo…

【MySQL进阶】常用MySQL程序

目录 一. mysqlcheck——表维护程序 1.1.作用 1.2 注意事项 1.3 使用方法 1.4 常用选项 1.5 mysqlcheck的特殊使用 二. mysqldump——数据库备份程序 2.1.作用 2.2 注意事项 2.3 使用方法 2.4 常用选项 三. mysqladmin——MySQL 服务器管理程序 3.1.作用 3.2 使用…

EMQX高效存储消息到MySQL指南

配置 EMQX 存储消息到 MySQL EMQX 可以通过规则引擎和数据桥接功能将消息和事件存储到 MySQL 数据库。以下是具体实现方法&#xff1a; 创建 MySQL 数据表 在 MySQL 中创建用于存储消息的表结构&#xff1a; CREATE TABLE mqtt_messages (id int(11) NOT NULL AUTO_INCREME…

springboot项目,利用docker打包部署

Windows WSL2 Docker Desktop 部署 SpringBoot 项目指南 &#xff08;没有安装docker的&#xff0c;如果是windows家庭中文版的&#xff0c;可以看我上一篇帖子&#xff1a;windows家庭版安装docker和redis-CSDN博客&#xff09; 本教程将说明如何在 Windows 系统 下&#…

MO+内核32位普冉单片机PY32F003开发板

PY32F003开发板是基于普冉半导体PY32F003微控制器设计的低成本入门级开发工具&#xff0c; PY32F003系列微控制器采用高性能的 32 位ARM Cortex-M0内核&#xff0c;宽电压工作范围的 MCU。嵌入高达32Kbytes flash 和 4Kbytes SRAM 存储器&#xff0c;最高工作频率 32MHz。PY32…

MySql 用存储过程删除所有用户表

用拼接语句总是会出问题 -- 1. 禁用外键约束&#xff08;防止级联删除失败&#xff09;[1]() SET SESSION FOREIGN_KEY_CHECKS 0; -- 2. 生成并执行删除语句&#xff08;替换 your_database_name&#xff09; SELECT CONCAT(DROP TABLE IF EXISTS , TABLE_NAME, ;) -- 预览语…