那谁的技术博客

感兴趣领域:高性能服务器编程,存储,算法,Linux内核
随笔 - 210, 文章 - 0, 评论 - 1183, 引用 - 0
数据加载中……

解读google C++ code style谈对C++的理解

C++是一门足够复杂的语言.说它"足够复杂",是因为C++提供了足够多编程范式--泛型, 模板, 面向对象, 异常,等等.顺便说说,我已经很久没有跟进C++的最新发展了(比如C++0x), 所以前面列举出来的特性应该只是C++所有特性的一个部分罢了.C++特性过多很难驾驭好C++的原因之一.另一个原因是C++过于"自作聪明",在很多地方悄无声息的做了很多事情, 比如隐式的类型转换, 重载, 模板推导等等.而很多时候,这些动作难以察觉,有时候会在你意想不到的地方发生,即使是熟练的C++程序员也难免被误伤.(关于了解C++编译器自作聪明做了哪些事情, <<深入理解C++物件模型>>是不错的选择).

世界上有很多问题, 人们知道如何去解决.但是, 似乎这还不算是最高明的,更高明的做法是学会避免问题的发生.而如何避免问题的发生, 需要经验的积累--曾经犯下错误,吃一堑长一智,于是知道哪些事情是不该做的或者是不应该这么做的.

google C++ code style是google对外公布的一份google内部编写C++的代码规范文档.与其他很多我曾经看过的编码文档一样,里面有一些关于代码风格的规定,也就是代码的外观,这一部分不在这里过多讨论,毕竟代码如何才叫"美观"是一个见仁见智的话题.在这里专门讨论这份文档中对一些C++特性该如何使用的讨论,最后再做一个总结.注意其中的序号并不是文档中的序号,如果要详细了解,可以自己去看这份文档.

1) Static and Global Variables
   Static or global variables of 
class type are forbidden: they cause hard-to-find bugs due to indeterminate order of construction and destruction.
google明确禁止全局对象是类对象, 只能是所谓POD(Plain Old Data,如int char等)数据才行.因为C++标准中没有明确规定全局对象的初始化顺序, 假设全局类对象A,B,其中A的初始化依赖于B的值, 那么将无法保证最后的结果.如果非要使用全局类对象, 那么只能使用指针, 在main等函数入口统一进行初始化.

2) Doing Work in Constructors
In general, constructors should merely 
set member variables to their initial values. Any complex initialization should go in an explicit Init() method. 
文档规定, 在类构造函数中对类成员对象做基本的初始化操作, 所有的复杂初始化操作集中一个比如Init()的函数中,理由如下:
  • There is no easy way for constructors to signal errors, short of using exceptions (which are forbidden).
  • If the work fails, we now have an object whose initialization code failed, so it may be an indeterminate state.
  • If the work calls virtual functions, these calls will not get dispatched to the subclass implementations. Future modification to your class can quietly introduce this problem even if your class is not currently subclassed, causing much confusion.
  • If someone creates a global variable of this type (which is against the rules, but still), the constructor code will be called before main(), possibly breaking some implicit assumptions in the constructor code. For instance, gflags will not yet have been initialized.
简单的概括起来也就是:构造函数没有返回值, 难以让使用者感知错误;假如在构造函数中调用虚拟函数, 则无法按照使用者的想法调用到对应子类中实现的虚拟函数(理由是构造函数还未完成意味着这个对象还没有被成功构造完成).

3) Default Constructors
You must define a 
default constructor if your class defines member variables and has no other constructors. Otherwise the compiler will do it for you, badly. 
当程序员没有为类编写一个默认构造函数的时候, 编译器会自动生成一个默认构造函数,而这个编译器生成的函数如何实现(比如如何初始化类成员对象)是不确定的.这样,假如出现问题时将给调试跟踪带来困难.所以, 规范要求每个类都需要编写一个默认构造函数避免这种情况的出现.

4) Explicit Constructors
Use the C
++ keyword explicit for constructors with one argument.
假如构造函数只有一个参数, 使用explicit避免隐式转换, 因为隐式转换可能在你并不需要的时候出现.

5) Copy Constructors
Provide a copy constructor and assignment 
operator only when necessary. Otherwise, disable them with DISALLOW_COPY_AND_ASSIGN.
只有当必要的时候才需要定义拷贝构造函数和赋值操作符. 同上一条理由一样, 避免一些隐式的转换.另一条理由是,"="难以跟踪,如果真的要实现类似的功能,可以提供比如名为Copy()的函数,这样子一目了然,不会像赋值操作符那样可能在每个"="出现的地方出现.

6) Operator Overloading
Do not overload operators except 
in rare, special circumstances.
不要重载操作符.同样, 也是避免莫名其妙的调用了一些函数.同上一条一样, 比如要提供对"=="的重载, 可以提供一个名为Equal()的函数, 如果需要提供对"+"的重载, 可以提供一个名为Add()的函数.

7) Function Overloading
Use overloaded functions (including constructors) only 
in cases where input can be specified in different types that contain the same information. Do not use function overloading to simulate default function parameters.
只有在不同的类型表示同样的信息的时候, 可以使用重载函数.其他情况下,一律不能使用.使用重载, 也可能出现一些隐式出现的转换.所以, 在需要对不同函数进行同样操作的时候, 可以在函数名称上进行区分, 而不是使用重载,如可以提供针对string类型的AppendString()函数, 针对int类型的AppendInt()函数,而不是对string和int类型重载Append()函数.另一个好处在于, 在阅读代码时,通过函数名称可以一目了然.

8) Exceptions
We 
do not use C++ exceptions.
不使用异常.理由如下:
  • When you add a throw statement to an existing function, you must examine all of its transitive callers. Either they must make at least the basic exception safety guarantee, or they must never catch the exception and be happy with the program terminating as a result. For instance, if f() calls g() calls h(), and h throws an exception that f catches, g has to be careful or it may not clean up properly.
  • More generally, exceptions make the control flow of programs difficult to evaluate by looking at code: functions may return in places you don't expect. This results maintainability and debugging difficulties. You can minimize this cost via some rules on how and where exceptions can be used, but at the cost of more that a developer needs to know and understand.
  • Exception safety requires both RAII and different coding practices. Lots of supporting machinery is needed to make writing correct exception-safe code easy. Further, to avoid requiring readers to understand the entire call graph, exception-safe code must isolate logic that writes to persistent state into a "commit" phase. This will have both benefits and costs (perhaps where you're forced to obfuscate code to isolate the commit). Allowing exceptions would force us to always pay those costs even when they're not worth it.
  • Turning on exceptions adds data to each binary produced, increasing compile time (probably slightly) and possibly increasing address space pressure.
  • The availability of exceptions may encourage developers to throw them when they are not appropriate or recover from them when it's not safe to do so. For example, invalid user input should not cause exceptions to be thrown. We would need to make the style guide even longer to document these restrictions!
上面提到的理由中, 我认为使用异常最大的害处就是:异常的使用导致了程序无法按照代码所展现的流程去走的, 比如代码里面写了步骤一二三,但是假如有异常出现, 这就不好预知代码真正步进的步骤了, 在出现问题时, 给调试和跟踪带来困难.
另外, 我更喜欢unix API的设计.熟悉unix编程的人都知道, unix API基本上都遵守下列规则:
a) 返回0表示成功, 其他(一般是-1)表示失败.
b) 在失败时, 可以根据errno判断失败的原因, 这些在man手册中都是会清楚的描述.

总结一下, 这份规范中规避的C++特性大致分为以下几类:
a) 避免使用那些没有确定行为的特性:如全局变量不能是类对象(初始化顺序不确定), 不使用编译器生成的默认构造函数(构造行为不确定), 异常(代码走向不确定).
b) 避免使用那些隐式发生的操作:如声明单参数构造函数为explict以避免隐式转换, 不定义拷贝构造函数避免隐式的拷贝行为, 不使用操作符重载避免隐式的转换
c) 对模棱两可的特性给予明确的规定:不使用函数重载而是定义对每个类型明确的函数.
d) 即使出错了程序也有办法知道: 比如不能在类构造函数中进行复杂的构造操作, 将这些移动到类Init()的函数中.

同时, 这份文档中描述的大部分C++特性, 都是我之前所熟悉的(除了RTTI之外, 不过这里提到它也是要说明不使用它,另外还提到boost, 不过也是说的要对它"有限制"的使用,比如里面的智能指针).可以看到, 面对这样一门复杂同时还在不停的发展更新特性的语言, google的态度是比较"保守"的.这与我之前对C++的理解也是接近的, 我一直认为C++中需要使用到的特性有基本的面向对象+STL就够了(经过最近的编码实践,我认为还得加个智能指针).我对这个"保守"态度的理解是, 以C++当前的应用场景来看, 这些特性已经足够, 如果使用其他一些更加复杂的, 对人的要求提高了, 代码的可读性以及以后的可维护性就下降了.

前面说过, 避免问题的出现比解决问题来的更加高明些, 而面对C++这一个提供了众多特性, google C++ code style给予了明确的规定, 也就是每个行为, 如果都能做到有明确的动作, 同时结果也都是可以预知的, 那么会将出问题的概率最大可能的降低, 即使出了问题, 也容易跟踪.

上面描述的并不是这份文档中有关C++的所有内容, 只不过我觉得这些更加有同感些, 详细的内容, 可以参看这份文档.都知道google的作品,质量有保证, 除了人的素质确实高之外, 有规范的制度保证也是重要的原因, 毕竟只要是人就会犯错, 为了最大限度的避免人犯错, 有一份详尽的代码规范, 写好哪些该做哪些不该做哪些不该这么做, 也是制度上的保证.另外, 假如每个人都能以一个比较高的标准要求自己所写的代码, 久而久之, 获得进步也是必然的结果.

从这套规范里面, 我的另一个感悟是, 不论是什么行业, "学会如何正确的做事情", 都是十分必要的.这个"正确的做事情", 具体到编码来说, 就是代码规范里面提到的那些要求.而除去编码, 做任何的事情, 使用正确的方式做事, 都是尽可能少的避免错误的方法.但是, "错"与"对"是相对而言的, 没有之前"错"的经历, 就不好体会什么叫"对".所以, "如何正确的做事", 说到了最后, 还得看个人的经验积累, 有了之前"错误"的经历,才能吃一堑长一智, "错误"并不是一无是处的, 只不过, 并不是谁都去尝试着从中学习.


posted on 2010-05-29 20:34 那谁 阅读(38014) 评论(43)  编辑 收藏 引用 所属分类: C\C++

评论

# re: 解读google C++ code style谈对C++的理解  回复  更多评论   

因噎废食
2010-05-29 22:59 | OwnWaterloo

# re: 解读google C++ code style谈对C++的理解  回复  更多评论   

@OwnWaterloo
又学了一个成语"因噎废食 ( yīn yē fèi shí )":原意是说,因为有人吃饭噎住了,索性连饭也不吃了,这太荒谬了。比喻要做的事情由于出了点小毛病或怕出问题就索性不去干

能解释一下在这里针对这个帖子做这个回复的含义么?

2010-05-29 23:02 | 那谁

# re: 解读google C++ code style谈对C++的理解  回复  更多评论   

@那谁
"C++某特性不理解,产生畏错心理(或者已经得到教训);
对此采取的措施不是去学习与理解这些特性, 而是放弃这些特性。"


对筷子不熟悉,不尝试学习与熟练,而是放弃 —— 这也不是不行, 毕竟用刀叉(甚至手抓)也算是吃饭 —— 但是, 如果是中餐的话, 这并不是什么值得为之感到光荣的事。


吃饭不同于软件开发的地方是:
餐桌上的同伴吃中餐不用筷子最多最多让人感到难堪。
而项目中的队员如果做不到步调一致, 就可能导致项目失败。
但是, 相比这饭吃得是否得体, 步调一致更为重要。
所以, 为了那些因噎废食的人, 不得不同他们一起"吃手抓饭", 从而达到步调一致, 也是很无奈的事。
—— 这依然不是什么值得炫耀的事情。


在正常控制流中插入的另一条控制流是异常的关键。
正是这条非寻常的控制流, 导致学习成本。
如果愿意去学习这条控制流的规则, 就能得到这条控制流带来的好处:
它确实将“错误的检测与处理”给分开了。
—— 中餐是否值得吃,就是在学习成本与所有层次上的所有错误都必须“手工地”检测并向上报告(即使不处理任何错误)之前做选择。


越来越多的语言加入异常处理机制(甚至是一些原本没有的语言)。
并且, 在正常控制流之外的控制流并不只在异常处理中出现: 信号, thread_cancel都有,

要么“永远逃避所有使用非正常控制流的技术”。
—— 永远不去学习如何使用筷子。

要么去学习任意一种, 并举一反三。
—— 学习筷子的使用后, 品尝中餐的诸多菜系都有基础了。


关于google, 我感到困惑的是: 对google中的python和java程序员是否也有不许使用异常的规定。
我想应该是没有的。
那么, 换来的结论就是: goolge中的C++程序员, 对异常这种编程思想的理解, 还不如google中的python和java程序员。
2010-05-29 23:59 | OwnWaterloo

# re: 解读google C++ code style谈对C++的理解  回复  更多评论   

@那谁
一说就起劲了……

再说说操作符重载。 equals add vs ==, +

记号系统的严谨与简洁都很重要。
以前我也只注重记号系统的严谨, 认为严谨的就是易读、易理解的。
直到读了这篇文章:
http://www.ibm.com/developerworks/cn/xml/x-matters/part24/index.html

清单3中的xml就是严谨而不简洁的。 即使不学习任何新事物, 也大致能猜出清单3表示的是什么内容。
而清单2的记号系统就是简洁的, 同时也是严谨的 —— 只是需要学习这套记号系统。

如果不学习清单2中的记号系统, 那清单2可能就是天书。
如果学习了, 那清单2就几乎是"用极少废话作辅助, 表达作者的意思"。

而清单3,无论是否学习, 都是很罗嗦的在表达作者的含义, 包含了太多的噪音。

我觉得(只是觉得, 因为历史不能重演), 如果微积分的记号系统不是随微积分一起产生, 微积分是发展不起来的。
将表达式中的Sx 全部替换成 interal(x); 记号是不需要学了, 但这表达式根本没法读。

所以, 不是简洁的记号系统不可取, 而是记号系统在简洁的同时还要保持直观, 严谨, 无歧义。
要设计出这样的记号系统是需要精心的、 全局的、 周详的准备。
而C++的操作符重载让记号的引入变得太容易, 获得简洁的同时容易丢失其他方面的因素。

上面回复已经说到google中C++程序员的素质。
与其于让他们理解直观、严谨、无歧义, 直接让他们不许使用新的记号系统可能更省事。
但无论如何, 这所谓的"高标准"的规范, 其实只是"无奈"之举, 而非"进步"。
2010-05-30 00:23 | OwnWaterloo

# re: 解读google C++ code style谈对C++的理解  回复  更多评论   

目前 Google 公开的大量 C++ 代码都遵循了这份规范。
这些公开的代码的质量都很高,值得借鉴。
2010-05-30 08:36 | 陈硕

# re: 解读google C++ code style谈对C++的理解  回复  更多评论   

@OwnWaterloo
那你能对规范中描述的异常的缺陷进行回复么?我里面提到的,由于引入了异常,导致代码走向难以预测,这一点如何解决?类似unix API那样的,根据返回值判断是否成功,根据errno判断失败原因,是非常简洁明了的做法,我更倾向于设计出这样的API.
2010-05-30 08:43 | 那谁

# re: 解读google C++ code style谈对C++的理解  回复  更多评论   

@OwnWaterloo
另外,我见到的google开源出来的python代码不多,java我不会,就更没看过了,但是这份python代码:
http://google-styleguide.googlecode.com/svn/trunk/cpplint/cpplint.py
里面似乎就没有使用到异常.
2010-05-30 08:45 | 那谁

# re: 解读google C++ code style谈对C++的理解  回复  更多评论   

@那谁
"我里面提到的,由于引入了异常,导致代码走向难以预测,这一点如何解决?"
你这个问题就像:
由于引入了指针(甚至双指针), 导致代码难以理解,充满bug, 这一点应该如何解决?
难道我们应该放弃指针(和双指针)?

"指针"还可以替换成很多东西。

一句话, 不学, 什么都难以理解。
如何解决?
1. 让代码保持异常安全 —— 无论异常是否发生。
什么游戏都需要规则,这是使用异常的最关键规则。
pthread_cancel有相应的cancellation safety。
信号有相应的可重入。

2. 如果没有异常需要处理, 让异常直接向上报告 —— 无须任何额外代码
而使用状态代码, 即使某个层次不能处理错误, 也必须编写代码去检测, 并向上报告(除非想吞掉这个错误)。
"每一层", "每一个调用点"。

状态代码不优美的地方之一 —— 太多重复代码, 这些代码并不是为了处理这个错误, 仅仅是为了向上报告错误, 所以不得不编写代码去检测。

不优美的地方之二 —— 检测代码和happy path代码混杂在一起, 使得happy path中至少一半代码都是和逻辑无关的, 所谓的噪音。
上面记号系统中说过, 积分记号确实需要学习成本, 但换来的好处就是用最少的噪音表达出尽可能多的信息。

不优美的地方之三 —— caller和callee之间报告错误所使用契约的耦合。
当caller是一个普通的函数这个问题还不突出。当caller是一个函数模板, 对应的callee根本不知道是何物时, 如何定义callee和caller之间的报告错误的契约?
"0表示Ok, 非0表示有问题, 问题写在errno" 而errno这种全局的东西带有全局的一切毛病, 比如:如何分配一个独一无二的code?

3. 如果需要处理当中的部分异常, try之, 并仅仅catch感兴趣的异常, 处理之。
接上面的, 如果不能得到一个独一无二的code, 就无法知道究竟是出了什么毛病。
EINVAL? 哪一个参数有问题? 什么问题? 信息早就丢了。
无法有针对性的进行处理。


更多的, 请看:
http://blog.csdn.net/pongba/archive/2007/10/08/1815742.aspx


而google coding style中所谓的不使用exception的理由, 在知道什么是异常处理的人的眼中, 只有最后2点是站得住脚的:
最后一点, 也就是上一个回复说的"与其规定什么时候可以用, 应该如何用", 不如直接"全面禁止" —— 因噎废食。

倒数第2点, 就是效率问题。 在"效率攸关"的地方, 确实不能用。
但这种地方并不是想象中的那么多。

其他都是扯谈。 要么写这个规范的人不懂exception, 要么就是他懂, 但故意牵强附会, 或者危言耸听。


另外, 这种高压政策下的google的代码我没看过, 所以无法评论其质量高低。
但肯定是"原始且不美观"的。
而这种在高压政策下产生出的"原始且不美观"的其他代码我倒是看过, 例如OpenCV和apr中的内存管理部分 —— 在这种"泯灭人类的思维与创造性, 仅仅将人作为制造code的工具"的高压政策下压榨出的代码, 质量怎么可能高?
2010-05-30 20:11 | OwnWaterloo

# re: 解读google C++ code style谈对C++的理解  回复  更多评论   

@OwnWaterloo
我反对你以偏概全将使用异常的态度推及到指针使用,这不是具体问题具体分析的态度.
其次,你的语气有些过于狂妄了.就结果而言,google出品的这些软件,虽然大部分都看不到代码,从使用的角度看,质量是有目共睹的,反之看它们的一些规定做法,有可取之处,而不是像你所言的"质量怎么可能高".
我还是坚持我的看法,异常的使用导致了程序的走向难以从代码中一目了然的看出来,给问题定位带来困难.
2010-05-30 20:33 | 那谁

# re: 解读google C++ code style谈对C++的理解  回复  更多评论   

@那谁
我说的是OpenCV和apr的内存管理部分的质量, 请仔细看。

还是那句话: 不学, 什么都困难。
这种所谓的"C++应该避免使用异常"和java程序员所谓的"应该避免使用指针, 所以我们使用java"有什么区别?


"异常的使用导致了程序的走向难以从代码中一目了然的看出来,给问题定位带来困难." —— 而这才是你的偏见。
就你这句话就能推断你没什么使用异常的经验, 而是出于对异常的不理解与恐惧。
请问我说错没有?
2010-05-30 20:42 | OwnWaterloo

# re: 解读google C++ code style谈对C++的理解  回复  更多评论   

@OwnWaterloo
异常少量使用过,已经基本不用,观点已经在前面阐述过.如果一个特性,我认为会给我带来困扰,同时目前所掌握的,已经可以满足我的需求,为什么我还要花时间去多学呢.有时候选择过多,反而会带来困扰吧.这个是我的观点,无意强加于谁身上.同时,他人也无需强加于我身上.

"另外, 这种高压政策下的google的代码我没看过, 所以无法评论其质量高低。
但肯定是"原始且不美观"的。"

这句话里面的语调,我个人认为过于狂妄了,呵呵.

如果你不能心平气和的讨论问题,而使用一些自己臆断的词汇去描述,比如"原始切不美观","质量怎么可能高",我个人认为继续下去的意义不大.
2010-05-30 20:50 | 那谁

# re: 解读google C++ code style谈对C++的理解  回复  更多评论   

@那谁
java程序员也可以说自己"基本用不着指针", "手工内存管理给我带来了很多困扰"。

是否美观并不是我臆断, 这可以使用完成同样功能(当然, 除了效率、以及二进制兼容行上不同)的代码进行对比。 只是cppblog对贴代码并不友好。
所以, 我给出了刘未鹏一篇文章的链接。
里面有完成同样事情, 错误代码如何做 vs 异常如何做 的代码片段。

如果你对异常处理没经验, 错误代码肯定很有经验吧?
1. 即使不能处理, 也必须检测 —— 因为需要向上层报告
2. 这些检测, 重复出现在每个层次, 每个调用点
3. 检测代码与逻辑代码混杂
4. 即使错误最终报告到一个可以处理的层次, 信息早就丢得7788了, 只剩下EINVAL这种含义模糊, 不知道应该如何应对的错误代码。

这些是否是实情?难道你不觉得这些手工的机械重复是不美观的?
而异常处理就是为了解决这些问题。


原始也不是臆断。 理由上面也有: 越来越多的语言开始加入异常机制。
C++很早就加入了, 却被程序员不分青红皂白的拒之门外, 是否原始?
2010-05-30 21:05 | OwnWaterloo

# re: 解读google C++ code style谈对C++的理解  回复  更多评论   

其实我觉得google禁止异常可能是处于与C代码互操作困难的考虑,而不仅仅是新特性带来的问题。如果是最终程序那怎么都好说,但作为一个库,跟其他代码如何进行跨编译单元的异常传递和处理恐怕不是那么简单。这是C++保留C连接能力必须付出的代价。而java显然没有这种问题。
2010-05-31 16:45 | ptptt5

# re: 解读google C++ code style谈对C++的理解  回复  更多评论   

额。。。最后一个。。。
2010-05-31 17:33 | 欣萌

# re: 解读google C++ code style谈对C++的理解  回复  更多评论   

@那谁
@OwnWaterloo
1.在我的眼里,throw就如同if,如同for,如同while,如同break,如同continue,如同return,仅仅是一种跳转机制。

2.市面上所说的throw的危险程度,排除了因为程序员的畏惧而不学习之外,唯一说的过去的无非就是throw很难在ABI上得到兼容。不过google在服务器端编译,因此不存在这种问题。存在的话是google的代码版本管理(这其中也包含编工具版本管理)的缺陷而不是throw的缺陷

3.throw带来类型系统的美,带来错误与处理的解耦。就如同linus所说,因为C++太难学(对于他本人可能不一定),因此禁止在linux里面使用C++。google的意思可能也是如此。我说的仅仅是可能,因为C++高手不多,反正招进去的都不太信任,所以干脆就禁掉算了。Microsoft相反,不会就学。

4.throw在学术上也是一个优美的类型系统的范例。当然这跟我们开发项目可能没什么大的关系,但这却是必然会出现的。原因我就不多说了,因为也是非工程的。不过随着越来越多的语言出现,我们看可以看到基本上所有东西都支持异常,而且也用得很好,为啥就唯独C++不行呢?因为他们心里没谱,没有catch(Exception e)这种万能东西可以用,而且团队也不肯花成本去研究所使用的框架的异常规则并制定他们自己的异常守则(没时间估计是一个原因,像google和baidu和bing这类公司底下员工加班已成惯例),所以最赚钱的办法就是——禁止

结论:google禁止异常比较省钱。
2010-05-31 19:22 | 陈梓瀚(vczh)

# re: 解读google C++ code style谈对C++的理解  回复  更多评论   

@陈梓瀚(vczh)

>>1.在我的眼里,throw就如同if,如同for,如同while,如同break,如同continue,如同return,仅仅是一种跳转机制。
而且, 这种跳转机制我认为比goto/longjmp要好理解, 因为它是结构化的。

>>2.市面上所说的throw的危险程度,排除了因为程序员的畏惧而不学习之外,唯一说的过去的无非就是throw很难在ABI上得到兼容。
嗯, exception真正从技术上不能被使用的原因就只有效率和二进制兼容性。
其他都是"人文原因", "市场原因","成本原因", "金钱原因"。

btw: 你发觉C++的二进制兼容性会闯祸了~?


>>4. ... 因为他们心里没谱,没有catch(Exception e)这种万能东西可以用 ...
这倒不难, 敢用exception肯定要人为规定一个最终基类。
即使没有这样的基类, 还可以catch( ... )
2010-05-31 21:48 | OwnWaterloo

# re: 解读google C++ code style谈对C++的理解  回复  更多评论   

@OwnWaterloo
我一直都知道兼容性有问题,但是一个团队使用相同的编译器是应该的,而且如果他们使用开源的库的话,自己编译更加安全。

其他语言的Exception都有Message,这个可是好东西啊……而且catch(...)不能处理基本错误,譬如说access violation,还有divided by zero等等。
2010-05-31 23:17 | 陈梓瀚(vczh)

# re: 解读google C++ code style谈对C++的理解  回复  更多评论   

@陈梓瀚(vczh)
>>但是一个团队使用相同的编译器是应该的
这也就暗示了C++库最方便的复用形式是源代码, 而不是二进制。


>>其他语言的Exception都有Message,这个可是好东西啊……而且catch(...)不能处理基本错误,譬如说access violation,还有divided by zero等等。
其他语言可以 throw 1212么? 1212怎么message?
SEH可以处理那2种, 还可以转化为C++ exception。 但是, 非Windows下?

其他语言是通过"限制能够被throw的对象的类型" —— 比如必须继承自Exception;
来达到使用catch (Exception e)就可以处理所有异常的目的。

C++没有这一限制, 需要自我限制。


其他语言是通过某些平台相关的机制: 比如SEH来处理access violation;
或者是通过损失基础运算的效率: 比如
T& dereference(T* p) { if (p) return *p; throw nullptr_exception(); }
unsigned plus(unsigned x, unsigned y)
{
if (UINT_MAX-x>y) return x+y;
throw overflow_exception();
}
来得到这些错误的异常。

C++不会添加这些保姆工作。


所以, 其实也不算C++ exception相对于其他语言的劣势, 只能算一种权衡。
默认情况下, 不限制可抛出类型, 可以通过catch (...)来处理所有。
如果需要, 可以自我规定一个基类。

默认情况下, 不检测基本运算的错误。
如果需要, 自己检测并抛出。
2010-06-01 01:30 | OwnWaterloo

# re: 解读google C++ code style谈对C++的理解  回复  更多评论   

引用:“当程序员没有为类编写一个默认构造函数的时候, 编译器会自动生成一个默认构造函数,而这个编译器生成的函数如何实现(比如如何初始化类成员对象)是不确定的”

老兄能详细解释一下否?我不明白你这话里要说的意思,能否具体说说你担心的“不确定”?
2010-06-01 09:54 | Kenny Yuan

# re: 解读google C++ code style谈对C++的理解  回复  更多评论   

@Kenny Yuan
我的意思是编译器生成的构造函数不知道会给类成员对象赋什么初值.如果自己写的话,给它们赋一个明确的初值,这样在出问题的时候一看,知道这些都是没有被初始化过的.
2010-06-01 10:29 | 那谁

# re: 解读google C++ code style谈对C++的理解  回复  更多评论   

@楼主

明白你说的意思了,看来我们是在“类成员对象”中的“对象”一词上的理解不一致了,呵呵

2010-06-01 11:02 | Kenny Yuan

# re: 解读google C++ code style谈对C++的理解  回复  更多评论   

@Kenny Yuan
哦,怎么说?谈谈你的理解.
2010-06-01 11:03 | 那谁

# re: 解读google C++ code style谈对C++的理解  回复  更多评论   

@那谁
默认构造函数的行为是"可预测"的。
就在你文章第1段中提到的书"inside C++ object model"中就有。
最关键的一句话: "编译器合成的构造函数, 只为满足C++的需要, 而不是程序员的需要。"


所以, 默认构造函数的行为是"可预测的", "确定的", 即 —— 具有trivial construct语意的member, 不会被初始化, 而是包含随机值。


关于随机值的两种处理:
FILE* f;
...
f = fopen( ... );

还是
FILE* f = 0;
...
f = fopen( ... );


后一种所谓的"规范写法", 在我看来完全是多此一举。
当然, 如果你真的需要这种多余的动作:
struct T {
... no constructor
};

T v = T(); // 不再是随机值。 v中的每个值都是确定的。
2010-06-01 17:48 | OwnWaterloo

# re: 解读google C++ code style谈对C++的理解  回复  更多评论   

boost::value_intialized<T>(x)
2010-06-02 12:29 | 空明流转

# re: 解读google C++ code style谈对C++的理解  回复  更多评论   

@楼主

C++标准关于ctor中成员的初始化顺序/值/copy ctor/assign operator等都有详细规定。你说的对象二字,在原句中,无论从狭义还是广义上都不成立。但当你解释完之后,我就知道你想要说的是哪种情形了(从写法上看是调用者需要使用trivial ctor)。有什么疑问大家都回家看标准吧,我不多写了(楼上有人写了不少了)
2010-06-02 19:06 | Kenny Yuan

# re: 解读google C++ code style谈对C++的理解[未登录]  回复  更多评论   

@OwnWaterloo
又一个技术流的家伙。c++中难学难用是出了名的。
有几个人啃过c++标准,几个人的代码量超过几万行的?
软件开发是一个工程,一个大家公认的简单清晰的代码规范,比使用所谓的高级特性给开发效率带来的提升更多。
2010-06-12 08:39 | dudu

# re: 解读google C++ code style谈对C++的理解  回复  更多评论   

@dudu
>> 一个大家公认的简单清晰的代码规范

异常对于你们不清晰? 怪谁? 怪你没入"技术流"?

并不是大家公认, 使用异常的语言是越来越多, 只是你们不求进步而已。
要说公认, 也就是固步自封流公认罢了。
2010-06-12 08:51 | OwnWaterloo

# re: 解读google C++ code style谈对C++的理解  回复  更多评论   

路过,学习了
2010-06-12 10:28 | 溪流

# re: 解读google C++ code style谈对C++的理解  回复  更多评论   

就想linus说的,C++是门让人恐慌的语言,而更让人恐慌的的是一些不专业的程序员正在使用它,google或许是从大局上考虑,毕竟这么大的一个公司能完全协调的都使用好异常等特性是比较难的,而现在这种激烈竞争的环境下,他们选择保守,出半点差错不起的啊。。。
2010-06-26 10:42 | buffer

# re: 解读google C++ code style谈对C++的理解  回复  更多评论   

的确,有些c++程序员固步自封,自以为是得太厉害了,抗拒很多现代语言的东西。
2010-06-29 10:08 | donglongchao

# re: 解读google C++ code style谈对C++的理解  回复  更多评论   

看到各位反对google的理由,感到十分亲切,跟我当年想得一模一样。
成长总需要过程,没见过哪个程序员夸耀自己的当年的远见卓识,都是在反省自己的很傻很天真。
2011-03-15 14:04 | dayn9

# re: 解读google C++ code style谈对C++的理解  回复  更多评论   

@dayn9
不要装,好吗?有什么话说出来,大家都可以受教~
2011-03-15 21:32 | 溪流

# re: 解读google C++ code style谈对C++的理解  回复  更多评论   

中文社区果然全是喷子。。。
2011-08-25 13:09 | lord

# re: 解读google C++ code style谈对C++的理解  回复  更多评论   

我觉得异常也好,rtti,既然用了c++就应该是要使用的。既然stl的错误处理都是用异常实现的,难道连stl都不用了么?
确实c++中有一些语言特性被公认标记为无用,例如异常规范与导出模板,但是异常绝不是其中之一。
合情合理的使用语言特性才是一个真正的程序员应该做的事情。
2011-08-28 22:03 | watsonsong

# re: 解读google C++ code style谈对C++的理解  回复  更多评论   

mark...
2011-08-30 13:06 | captivated

# re: 解读google C++ code style谈对C++的理解  回复  更多评论   

@OwnWaterloo
编码规范是一种控制质量的手段,制定者希望通过微调规范的每个细节让整个产品的损益值达到最优化,所以虽然有很多高手能把exception玩的滴水不漏,但作为整个产品系编码规范的制定者,必须考虑到还有其他水平并不那么出色的开发者,另外,不知道你有没有仔细读过google列出的这些反对使用exception的观点,很明显的是,有些问题并不是技术牛逼就能解决的,很显然excepion的完美使用过于繁琐,而人又是容易犯错的,所以在编译器或者说语言本身没办法提供更智能化更安全纠错机制的前提下,禁止使用exception也是非常值得理解的。最后,我又仔细看了你的回复内容,发现你并没有真正理解google的观点,关于编写异常安全代码,可以看一下exceptional C++的第二章,然后再来这里发言吧。
2011-09-26 23:24 | stepinto

# re: 解读google C++ code style谈对C++的理解  回复  更多评论   

@stepinto
上面有条回复正中要害:
>> 结论:google禁止异常比较省钱。

那么,你们禁止使用(了解)异常(以及其他各种技术)是为了什么呢?
为了当你所说的那种
>> 水平并不那么出色的开发者
对吗? 你就甘愿当这种拖后腿的人,是吗?

>> 可以看一下exceptional C++的第二章,然后再来这里发言吧。
不好意思,整个exceptional系列我都看过好多年了。
你想得到的与想不到的C++书籍我都看过。
你想得到的与想不到的C++技术我都玩过。
所以我才看不起你们这种 *为自己的无能找借口* 的人。
2011-09-26 23:51 | OwnWaterloo

# re: 解读google C++ code style谈对C++的理解  回复  更多评论   

@OwnWaterloo
晕,感觉是被那本“拒绝借口”给洗脑了。。。。
2011-09-27 10:57 | stepinto

# re: 解读google C++ code style谈对C++的理解  回复  更多评论   

Google并没有说异常就是不好。使用异常有好处,也有坏处。Google不推荐使用的最大原因是现有代码使用异常的很少,也就是兼容性的原因。Style Guide里面也承认如果重头开始一个项目,使用异常是利大于弊的。
2011-10-14 10:06 | Zor X.L.

# re: 解读google C++ code style谈对C++的理解  回复  更多评论   

想请问一件事
若果使用异常处理,通常隐含着代码树中会有非常多的异常类的档案出现
那么有什么比较有系统的方式维护这些众多的异常类呢?
2011-10-23 23:56 | UGP

# re: 解读google C++ code style谈对C++的理解  回复  更多评论   

@UGP
另外补充一篇
http://chinesetrad.joelonsoftware.com/Articles/Wrong.html
2011-10-24 14:21 | UGP

# re: 解读google C++ code style谈对C++的理解  回复  更多评论   

@UGP
joel这篇文章足以暴露他思维层次太低。


即使是在C语言中,对i+j,同样不能确定它究竟做了什么。
完全被优化掉了?
还是上下文中有重复计算,此处直接取的结果?
但有多少人是真正关心i+j具体被如何处理,实现的么?
绝大多数情况都不需要。

那为什么对C++,就要求了解i+j是具体在干什么呢?
做出这样批评的人,都是 *只懂得C级别的抽象方式,不懂得C++级别的抽象方式* 而已。


对异常也是如此。
对自己熟悉的方式根深蒂固,以至于根本就无法恰当分析其他方式。
他的批评,比如让代码难以理解,熟悉异常的人的眼中完全不可理解。


而匈牙利更是扇自己嘴巴。
他要的就是将safe与unsafe字符串通过某种方式告诉编译器。
比如用不同类型,限制它们之间的自由转换,转换只能通过可控制的有限方式。
然后,让 *编译器自动地完成这样的检测* ,而不是什么手工肉眼去比。
2011-10-25 00:51 | OwnWaterloo

# re: 解读google C++ code style谈对C++的理解  回复  更多评论   

关于异常这块,你没有看完http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Exceptions 的说明

On their face, the benefits of using exceptions outweigh the costs, especially in new projects. However, for existing code, the introduction of exceptions has implications on all dependent code. If exceptions can be propagated beyond a new project, it also becomes problematic to integrate the new project into existing exception-free code. Because most existing C++ code at Google is not prepared to deal with exceptions, it is comparatively difficult to adopt new code that generates exceptions.

2013-05-07 17:00 | baibaichen

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理