北京网站seo服务,wordpress win2008 r2,企业所得税怎么征收比例,奉化网站关键词优化费用【学习笔记】C代码规范整理
一、匿名空间namespace
匿名命名空间#xff08;Anonymous Namespace#xff09;是一种特殊的命名空间声明方式#xff0c;其作用是将声明的成员限定在当前编译单元#xff08;源文件#xff09;内可见#xff0c;类似于使用 static 关键字修…【学习笔记】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
};
// 总大小1a7填充8b4c 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
};
// 总大小841 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; // C17所以一般正常的宏定义就可以直接用constexpr代替了。const就用作常量使用即可。
关键字常量性质初始化时机典型用途const运行时常量运行时初始化值在运行时确定如配置文件读取constexpr编译时常量编译时初始化值必须在编译期确定如数组长度、模板参数
八、nullptr 代替宏NULL
C11 之前宏NULL 代表空指针但是被定义为0存在类型歧义。采用C11 新增的关键字nullptr 代替NULL。
九、固定数组使用std::array 容器
C 11 新增了std::array 容器用来存放固定大小的数组访问元素时具有越界检查功能。
std::arrayint, 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::arrayint, 5 arr;
std::cout arr.size(); // 直接获取编译期常量安全高效十、动态数组使用std::vector
使用std::vector 代替new[]利用new[] 动态申请内存要用delete[] 释放容易发生内存泄漏。std::vector离开作用域自动释放。并且返回的指针本身并没有包含size 信息访问时不会进行越界检查。std::vector 可以自动管理内存并且访问内存时可以进行边界检查。
std::vectorint 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 无法实现
templatetypename T
using Vec std::vectorT;// 模板别名using 专属
templatetypename T
using MapString std::mapstd::string, T;MapStringint age_map; // 等价于 std::mapstd::string, int// 若用 typedef 实现相同功能需借助模板类typedef
templatetypename T
struct MapString {typedef std::mapstd::string, T type;
};MapStringint::type age_map; // 语法冗余十三、enum class代替enum
在 C 中enum class强类型枚举 是 C11 引入的特性用于替代传统的 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_castint(NewEnum::A)); // 必须显式转换显式底层类型
// 指定底层类型为 uint8_t节省内存
enum class Status : uint8_t {OK 0,ERROR 1,PENDING 2
};// 普通枚举无法指定底层类型可能浪费内存
enum OldStatus {OK, // 通常为 int4字节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;
};