5. 其他 C++ 特性
------------------------
5.1. 引用参数
~~~~~~~~~~~~~~~~~~~~
.. tip::
所以按引用传递的参数必须加上 ``const``.
定义:
在 C 语言中, 如果函数需要修改变量的值, 参数必须为指针, 如 ``int foo(int *pval)``. 在 C++ 中, 函数还可以声明引用参数: ``int foo(int &val)``.
优点:
定义引用参数防止出现 ``(*pval)++`` 这样丑陋的代码. 像拷贝构造函数这样的应用也是必需的. 而且更明确, 不接受 ``NULL`` 指针.
缺点:
容易引起误解, 因为引用在语法上是值变量却拥有指针的语义.
结论:
函数参数列表中, 所有引用参数都必须是 ``const``:
.. code-block:: c++
void Foo(const string &in, string *out);
事实上这在 Google Code 是一个硬性约定: 输入参数是值参或 ``const`` 引用, 输出参数为指针. 输入参数可以是 ``const`` 指针, 但决不能是 非 ``const`` 的引用参数.
在以下情况你可以把输入参数定义为 ``const`` 指针: 你想强调参数不是拷贝而来的, 在对象生存周期内必须一直存在; 最好同时在注释中详细说明一下. ``bind2nd`` 和 ``mem_fun`` 等 STL 适配器不接受引用参数, 这种情况下你也必须把函数参数声明成指针类型.
.. _function-overloading:
5.2. 函数重载
~~~~~~~~~~~~~~~~~~~~
.. tip::
仅在输入参数类型不同, 功能相同时使用重载函数 (含构造函数). 不要用函数重载模拟 `缺省函数参数 <http://code.google.com/p/google-gflags/>`_.
定义:
你可以编写一个参数类型为 ``const string&`` 的函数, 然后用另一个参数类型为 ``const char*`` 的函数重载它:
.. code-block:: c++
class MyClass {
public:
void Analyze(const string &text);
void Analyze(const char *text, size_t textlen);
};
优点:
通过重载参数不同的同名函数, 令代码更加直观. 模板化代码需要重载, 同时为使用者带来便利.
缺点:
限制使用重载的一个原因是在某个特定调用点很难确定到底调用的是哪个函数. 另一个原因是当派生类只重载了某个函数的部分变体, 会令很多人对继承的语义产生困惑. 此外在阅读库的用户代码时, 可能会因反对使用 `缺省函数参数 <http://code.google.com/p/google-gflags/>`_ 造成不必要的费解.
结论:
如果你想重载一个函数, 考虑让函数名包含参数信息, 例如, 使用 ``AppendString()``, ``AppendInt()`` 而不是 ``Append()``.
5.3. 缺省参数
~~~~~~~~~~~~~~~~~~~~
.. tip::
我们不允许使用缺省函数参数.
优点:
多数情况下, 你写的函数可能会用到很多的缺省值, 但偶尔你也会修改这些缺省值. 无须为了这些偶尔情况定义很多的函数, 用缺省参数就能很轻松的做到这点.
缺点:
大家通常都是通过查看别人的代码来推断如何使用 API. 用了缺省参数的代码更难维护, 从老代码复制粘贴而来的新代码可能只包含部分参数. 当缺省参数不适用于新代码时可能会导致重大问题.
结论:
我们规定所有参数必须明确指定, 迫使程序员理解 API 和各参数值的意义, 避免默默使用他们可能都还没意识到的缺省参数.
5.4. 变长数组和 alloca()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. tip::
我们不允许使用变长数组和 ``alloca()``.
优点:
变长数组具有浑然天成的语法. 变长数组和 ``alloca()`` 也都很高效.
缺点:
变长数组和 ``alloca()`` 不是标准 C++ 的组成部分. 更重要的是, 它们根据数据大小动态分配堆栈内存, 会引起难以发现的内存越界 bugs: "在我的机器上运行的好好的, 发布后却莫名其妙的挂掉了".
结论:
使用安全的内存分配器, 如 ``scoped_ptr`` / ``scoped_array``.
5.5. 友元
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. tip::
我们允许合理的使用友元类及友元函数.
通常友元应该定义在同一文件内, 避免代码读者跑到其它文件查找使用该私有成员的类. 经常用到友元的一个地方是将 ``FooBuilder`` 声明为 ``Foo`` 的友元, 以便 ``FooBuilder`` 正确构造 ``Foo`` 的内部状态, 而无需将该状态暴露出来. 某些情况下, 将一个单元测试类声明成待测类的友元会很方便.
友元扩大了 (但没有打破) 类的封装边界. 某些情况下, 相对于将类成员声明为 ``public``, 使用友元是更好的选择, 尤其是如果你只允许另一个类访问该类的私有成员时. 当然, 大多数类都只应该通过其提供的公有成员进行互操作.
5.6. 异常
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. tip::
我们不使用 C++ 异常.
优点:
- 异常允许上层应用决定如何处理在底层嵌套函数中 "不可能出现的" 失败, 不像错误码记录那么含糊又易出错;
- 很多现代语言都使用异常. 引入异常使得 C++ 与 Python, Java 以及其它 C++ 相近的语言更加兼容.
- 许多第三方 C++ 库使用异常, 禁用异常将导致很难集成这些库.
- 异常是处理构造函数失败的唯一方法. 虽然可以通过工厂函数或 ``Init()`` 方法替代异常, 但他们分别需要堆分配或新的 "无效" 状态;
- 在测试框架中使用异常确实很方便.
缺点:
- 在现有函数中添加 ``throw`` 语句时, 你必须检查所有调用点. 所有调用点得至少有基本的异常安全保护, 否则永远捕获不到异常, 只好 "开心的" 接受程序终止的结果. 例如, 如果 ``f()`` 调用了 ``g()``, ``g()`` 又调用了 ``h()``, ``h`` 抛出的异常被 ``f`` 捕获, ``g`` 要当心了, 很可能会因疏忽而未被妥善清理.
- 更普遍的情况是, 如果使用异常, 光凭查看代码是很难评估程序的控制流: 函数返回点可能在你意料之外. 这回导致代码管理和调试困难. 你可以通过规定何时何地如何使用异常来降低开销, 但是让开发人员必须掌握并理解这些规定带来的代价更大.
- 异常安全要求同时采用 RAII 和不同编程实践. 要想轻松编写正确的异常安全代码, 需要大量的支撑机制配合. 另外, 要避免代码读者去理解整个调用结构图, 异常安全代码必须把写持久化状态的逻辑部分隔离到 "提交" 阶段. 它在带来好处的同时, 还有成本 (也许你不得不为了隔离 "提交" 而整出令人费解的代码). 允许使用异常会驱使我们不断为此付出代价, 即使我们觉得这很不划算.
- 启用异常使生成的二进制文件体积变大, 延长了编译时间 (或许影响不大), 还可能增加地址空间压力;
- 异常的实用性可能会怂恿开发人员在不恰当的时候抛出异常, 或者在不安全的地方从异常中恢复. 例如, 处理非法用户输入时就不应该抛出异常. 如果我们要完全列出这些约束, 这份风格指南会长出很多!
结论:
从表面上看, 使用异常利大于弊, 尤其是在新项目中. 但是对于现有代码, 引入异常会牵连到所有相关代码. 如果新项目允许异常向外扩散, 在跟以前未使用异常的代码整合时也将是个麻烦. 因为 Google 现有的大多数 C++ 代码都没有异常处理, 引入带有异常处理的新代码相当困难.
鉴于 Google 现有代码不接受异常, 在现有代码中使用异常比在新项目中使用的代价多少要大一些. 迁移过程比较慢, 也容易出错. 我们不相信异常的使用有效替代方案, 如错误代码, 断言等会造成严重负担.
我们
Google C++规范(中文版)
需积分: 31 35 浏览量
2011-03-15
15:29:03
上传
评论
收藏 195KB ZIP 举报
ywwzq0507
- 粉丝: 28
- 资源: 11
最新资源
- HCIP-Datacom笔记 (1).pdf
- yolov5,SSD 可能使用到的一些代码
- bbbbbbbbbbbbbbbbbb
- 安卓逆向学习笔记之Frida Stalker 还原OLLVM AES.docx
- 安卓逆向学习笔记之unicorn来trace还原OLLVM Base64.docx
- 最新版本私钥助记词碰撞器大富豪使用python进行制作通过接口的方式进行验证支持多币种多链多网络一分钟万次验证高出货率
- 介绍离散性制造行业的MES系统流程
- Arduino IDE压缩包版本,2024年4月26日,最新版本
- 基于IDEA-CCNL/Randeng-Pegasus-238M-Summary-Chines微调的中文文本摘要任务源码+数据集
- 自动驾驶-状态估计和定位之直方图滤波(Histogram+Filter)定位应用和源码.pdf
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈