今晚在水木上看到一个帖子,我转载一下楼主的问题:
class CA { public: void a(){}; }; void fun() { CA a; int n1=sizeof(a); //这里n1是1,为什么? }
大家不要花太多时间去想上面这个问题啊。我知道这个问题有些吸引人,也许未来有一天你去某个知名企业面试,面试官会很得意的问你,这是为什么啊。但是,请真的不要过多的去想这个问题,为什么呢?因为在我接近9年的C++经验中(有接近5年的电信领域工作经验),我没有关注过sizeof。
问题接着就来了,为什么不关注呢,难道你不关注你对象的大小吗?我当然关注,而且很关注,可惜的是我明白sizeof(T)不能反应这个类型耗用的内存量,不信你可以sizeof(a_string_obj),你就知道我的意思了。那知道sizeof(n1) =1毫无用处吗?不是的,有一丁点,在你察看core dump文件的时候1/00000或者1/100000的时候用的上:) 不是知道它等于1有用(实际上标准没有规定它==1,而是知道它可能不等于0有用)。
C++真的很繁琐很复杂,但实际上大部分都是我们人为造成的:
(1) 很多书籍介绍了一些trick,当然可能N个教条里面确实有几条是有用的;
(2) 面试的时候喜欢问一些问题。譬如vtable如何实现的啊?
(3) 对知识的贪图心态。
其实造成这些问题的最大原因,就是我们没有想清楚问题。C++被设计出来是编程序的,写程序是为了解决实际的问题的。换句话说,C++,或者任何一们语言的最终目的是为了更好的帮你解决实际中的问题的。那么知道这些,就好办了。学习C++的过程中,反复的问自己或者有经验的工程师,如果用C++更好的解决你的问题,什么才是C++最本质的难点。我结合我实际工作经验给出一些我的看法:
(1) 内存管理,很难。不是你说的你new一个,忘记delete了;或者异常没弄好,你delete不了。那不是难,那是编程错误。难得是在:内存碎片,库里面的内存泄露或者错误读写。解决方法是:选用好的内存分配库或者GC。其实这个问题还没有这么简单,也许你的内存管理库或者GC会和你的标准库的内存分配策略矛盾,造成很多隐晦的Bug,非常难解决和定位。但是,这样做通常会比你自己去写内存分配池好很多,毕竟成熟的库用了很长时间了。
(2) 生命周期。这是一个很难的问题,不信你可以尝试实现一个Logger,你就会发现这个问题有多复杂。我们考虑一下,如果你在atexit注册的函数被调用的时候,都需要logger,你就会明白这件事情的复杂性。我们解决的方法就是,大家规定好初始化的顺序,譬如最先是logger,然后是数据库链接。释放的时候反方向,整个team都遵循。之后任何人如果想往里面添东西,都需要设计好很多用例来好好的测试。
(3) 内存模型。这个东西对于并发程序非常重要,比上面任何一个都重要。为什么?因为即使你跑了上百天压力测试,你也可能在上线后一天内core掉。我知道上百天测试之后,你的coverage已经很高了。解决方法:放弃你对violatile的信任,老老实实用锁。你可以使用mutex或者RWLock,或者condition, semphone都可以。如果你对性能很看重,使用平台专用的原语,memory barrier和atomic类型(但是后面的代码一定是十分少的,如果不是这样,你要审视你的设计)。使用一些Lint工具,有助于减少你犯错的可能。但是你唯一能信任的是:Lint报了错误的,你要认真检查;即使Lint全部过,依然可能有问题。
没有了?没有了。如果还有,那就是你需要放弃你内心“强大的求知欲望“,踏踏实实的做一个”普普通通“的C++程序员。我知道template很精彩,0x里面的static_assert, lamada很好玩,不过相信我,短时间内那个不属于工业界(除了内存模型和thread库)。还是那句话,直面你的问题,用最简单的设计和编码解决它,让你的设计和代码很容易被理解。(说到这里,也许有朋友会说,你自己还去看0x。其实你也可以去看,去学习,但是实际中请务必清醒的认识到,什么才是本质。如果你做不到这一点,那就不要去看了。因为看的越多,你越不会写代码)。