本指南旨在规范 Android C++ 开发,核心强调内存安全、接口清晰、跨平台性及高可维护性。通过工具强制执行命名、注释与格式规范,确保代码在 AOSP 规模下的高性能与稳健。

核心逻辑:Google 和 Android 团队通过推动版本升级,旨在利用现代 C++ 的特性来消除旧版本中常见的 Bug(如内存泄漏、未定义行为)。
为什么“禁止超出范围”?
开发建议:关注 Android.bp 文件中的 cpp_std 设置。如果你想用新特性,请先确认当前的 NDK 版本是否完全支持。
含义:指编译器(主要是 Clang)提供的一些“特有功能”,例如某些 GNU 特有的内建函数、特定架构的语法糖等。
为什么要严禁?
实操原则:如果你发现自己必须写类似 __attribute__((...)) 这种非标准关键字,请务必先思考:有没有标准的 C++ 替代方案? 如果没有,将其封装在特定的宏中,并标注清楚平台依赖。
int 一定是 32 位)。使用 <cstdint> 中的 <int32_t>, <uintptr_t> 等标准类型。.h 文件在包含其所有必要依赖后,应该能够单独通过编译(即 g++ -c my_header.h 不报错)。#include。没有防御机制,编译器会重复定义类、函数或变量,触发报错。#pragma once 虽然被广泛支持,但 Google C++ 指南基于可移植性(非标准)仍推荐使用宏防御。A.h 包含了 B.h,你绝不能因为引用了 A.h 就直接使用 B.h 中的符号。.cc 中使用了 std::vector,哪怕它在某个已包含的头文件中被间接引用了,你也必须在 .cc 中显式添加 #include <vector>。include 变得安全且简单,自动化构建工具(如 clang-tidy 的 IWYU 检查)能够精确工作。sizeof)时。#include,不能仅用前向声明。.cc 文件包含该头文件时,链接器会报“符号重定义”错误(Multiple Definition Error)。android::hardware::audio::。using namespace xxx;,这会强行污染包含该头文件的所有其他文件,引发不可控的命名冲突。namespace { ... })将代码的可见性限制在单个 .cc 文件内。.so 文件体积(符号表变小),并防止不同文件间的函数名重复。static 修饰或放入匿名命名空间。for 循环中定义的迭代器,不要提到循环外。main() 函数运行前,程序就已经因为互相依赖而 Crash。static std::vector 或 static MyClass 这种会导致运行时动态初始化的对象。thread_local 虽然能解决多线程数据隔离问题,但它会给编译器和链接器带来额外的复杂性,且在大量线程切换的 Android 服务中,频繁访问 thread_local 变量可能成为性能热点。Init() 方法。explicit 关键字。MyClass a = 10; 会将 10 隐式构造为 MyClass)。这经常导致极其隐蔽的 Bug。explicit,除非你明确希望该类型可以被隐式转换为你的类。= delete 明确禁用;如果需要,务必显式实现。struct:仅用于存放数据的集合(POD),没有任何复杂的函数逻辑。class:用于封装实现、维护不变量、提供接口的逻辑对象。struct 开始包含方法或复杂的逻辑,请立刻将其转换为 class。override 强制化。override:必须在重写虚函数时使用该关键字,防止因为函数签名微小差异(如 const 修饰符不同)导致并未实际覆盖父类方法。+ 号应该表现得像数学加法;如果 a + b 成立,通常也应实现 b + a 以保持对称。public -> protected -> private。这是一种约定俗成的视觉惯例,让阅读者先看到接口,再看到实现细节。const 引用(const T&)或值传递。const 指针(T*),因为指针本身就暗示了该参数会被修改,且调用时需要传地址,这为调用方提供了清晰的视觉警告。DoThis() 和 DoThat(),而不是 DoSomething(int) 和 DoSomething(string)。int Func())。template <typename T, typename U> auto Add(T t, U u) -> decltype(t + u))。unique_ptr/shared_ptr) :核心逻辑是所有权明确化。 unique_ptr 表示独占,shared_ptr 表示共享,严禁裸指针(raw pointer)跨越函数边界持有所有权。这从根源上杜绝了内存泄漏。std::vector 或 std::string)的不必要拷贝,通过移动语义提升性能。noexcept:Android 系统中异常会导致运行时开销过大(栈展开),且 noexcept 能提示编译器进行优化,并帮助调用者在处理关键逻辑时明确其安全性。Class A 与其对应的 A::Builder)之间使用,禁止滥用以防止类之间产生强耦合。static_cast 等) :C 风格转换(如 (int*)p)太危险且隐蔽,C++ 显式转换明确了转换意图(如 static_cast 是安全转换,reinterpret_cast 是不安全转换),便于 Code Review 审计。int32_t, uint64_t) :在 Android 这种横跨 ARM/x86 架构的系统中,int 的长度是不确定的,强制使用定宽整数是保证跨平台 ABI 一致性的基石。constexpr 系列:鼓励将逻辑从运行期迁移至编译期。这不仅能提升运行性能,还能在编译阶段通过 static_assert 捕获配置错误。nullptr:完全替换 0 或 NULL。在 Android 等强类型要求高的环境下,nullptr 避免了在重载函数匹配中产生歧义。auto 类型推导:原则是:若变量类型在上下文中显而易见,则用 auto;否则必须写明类型。 旨在提高代码可读性而非盲目追求简洁。dynamic_cast 和 typeid。它们依赖于庞大的运行时类型支持,在 Android 底层代码中,使用 RTTI 通常意味着架构设计上有缺陷,应通过多态接口重构解决。constexpr 函数、inline 函数或 enum class 代替。核心原则:通过名称即刻区分实体的性质。
| 实体类型 | 命名格式 | 示例 |
|---|---|---|
| 文件命名 | 全小写 + 下划线 | my_feature_controller.cc |
| 类型命名 | 大驼峰 (PascalCase) | class AudioBuffer |
| 变量命名 | 小写 + 下划线 | int packet_size; |
| 成员变量 | 小写 + 下划线 + 末尾下划线 | int packet_size_; |
| 常量命名 | k + 大驼峰 | const int kMaxRetryCount = 5; |
| 函数命名 | 小驼峰 (camelCase) | void processData(); |
| 命名空间 | 全小写 (单数) | namespace audio { ... } |
| 宏命名 | 全大写 + 下划线 | #define MAX_BUFFER_SIZE |
_) :这是为了在代码中一目了然地识别出“这是类成员,还是局部变量”。例如,当看到 packet_size_ 时,无需查找定义就能确认它是一个成员属性。k 前缀:这是 Google 的传统,通过 k 前缀明确区分普通的 const 变量和全局常量。核心原则:注释应解释“为什么 (Why)”和“约束 (Constraints)”,而非“是什么 (What)”。 代码本身已经解释了是什么,如果代码写得好,无需注释也能读懂;注释的价值在于解释代码背后的设计逻辑。
类注释:说明类的用途、线程安全约束(例如:“此类不是线程安全的,调用前需加锁”)。
函数注释:
delete)。TODO(username): 待办事项说明。这是一种非常有用的沟通工具,方便团队协作与进度追踪。if 或 lambda)中尤为重要,可以防止代码过早向右“溢出”。{ 紧跟在控制语句或类定义之后(不换行),右大括号 } 独占一行(除非是空的 if 或简单的闭包)。if、for 或 while 内部只有一行代码,也必须使用大括号 {}。这是防止悬挂 else 问题和后续维护中添加错误逻辑的最有效防御手段。& 和 * 修饰符应紧靠类型名,而不是变量名。
const string& name,int* ptr。& 和 * 是类型的一部分,而非变量名的一部分,符合 C++ 类型系统的语义。if, for, while, switch)后必须有一个空格。if (condition) 而非 if ( condition ))。case 块必须缩进。{} 包裹 case 内部逻辑。case 或 default 结尾必须有 break 或 return(除非有明确的 [[fallthrough]] 注释)。+, -, =, == 等)两侧均需加空格。核心逻辑:不因追求完美而破坏现有系统的稳定性。
new/delete 和裸指针,你强行植入智能指针可能会导致逻辑冲突或意想不到的内存生命周期问题。核心逻辑:为特定平台的底层实现提供合法化接口。
Windows API 或 asm 汇编)都必须被严密地封装在独立的源文件或抽象层中。#if defined(_WIN32))进行隔离,严禁在业务逻辑中零散嵌入平台特性。Android.bp/CMake)能正确处理这些依赖,且不会污染最终部署到 Android 设备的二进制文件。如果在此文章中您有所收获,请给作者一个鼓励,点个赞,谢谢支持
技术变化都很快,但基础技术、理论知识永远都是那些;作者希望在余后的生活中,对常用技术点进行基础知识分享;如果你觉得文章写的不错,请给予关注和点赞;如果文章存在错误,也请多多指教!