C++的坑响应式技术真的是个坑多吗

下次自動登錄
現在的位置:
& 綜合 & 正文
C++的坑真的多嗎?
先說明一下,我不希望本文變成語言爭論貼。希望下面的文章能讓我們客觀理性地了解C++這個語言。(另,我覺得技術爭論不要停留在非黑即白的二元價值觀上,這樣爭論無非就是比誰的嗓門大,比哪一方的觀點強,毫無價值。我們應該多看看技術是怎麼演進的,怎麼取捨的。)
周五的時候,我在我的微博上發了一個貼說了一下一個網友給我發來的C++程序的規範和內存管理寫的不是很好(後來我刪除了,因為當事人要求),我並非批判,只是想說明其實程序員是需要一些“疫苗”的,並以此想開一個“程序員疫苗的網站”,結果,@簡悅雲風同學:“不要用
C++ 直接用 C , 就沒那麼多坑了。”就把這個事帶入了語言之爭。
: 說C++比C的坑更多的人我可以理解,但理性地思考一下。C語言的坑也不少啊,如果說C語言有90個坑,那麼C++就是100個坑(另,我看很多人都把C語言上的坑也歸到了C++上來),但是C++你得到的東西更多,封裝,多態,繼承擴展,泛型編程,智能指針,……,你得到了500%東西,但卻只多了10%的坑,多值啊。
結果引來了更多的回復(只節選了一些言論):
那麼,C++的坑真的多麼?我還請大家理性地思考一下。
C++真的比C差嗎?
我們先來看一個圖——《》,從這個圖上看,C程序員比C++的程序員在注釋中使用fuck的字眼多一倍。這說明了什麼?我個人覺得這說明C程序員沒有C++程序員淡定。
不要太糾結上圖,只是輕鬆一下,我沒那麼無聊,讓我們來看點真正的論據。
相信用過C++的程序員知道,C++的很多特性主要就是解決C語言中的各種不完美和缺陷:(註:C89、C99中許多的改進正是從C++中所引進的)
(注意:上面我沒有提重載運算符和異常,前者寫出來的代碼並不易讀和易維護(參看《》後面的那個示例),坑也多,後者並不成熟(相對於Java的異常),但是我們需要知道try-catch這種方式比傳統的不斷地判斷函數返回值和errno形成的大量的if-else在代碼可讀性上要好很多)
上述的這些東西填了不知有多少的C語言編程和維護的坑。少用指針,多用引用,試試autoptr,用用封裝,繼承,多態和函數重載…… 你面對的坑只會比C少,不會多。
C++的坑有多少?
C++的坑真的不多,如果你能花兩到三周的時候讀一下《》里的那50多個條款,你就知道C++里的坑並不多,而且,有很多條款告訴我們C++是怎麼解決C的坑的。然後,你可以讀讀《》和《》,你可以了解一下C++各種問題的解決方法和一些常見的經典錯誤。
當然,C++在解決了很多C語的坑的同時,也因為OO和泛型又引入了一些坑。消一些,加一些,我個人感覺上總體上只比C多10%左右吧。但是你有了開發速度更快,代碼更易讀,更易維護的500%的利益。
另外,不可否認的是,C++中的代碼出了錯誤,有時候很難搞,而且似乎用C++的人會覺得C++更容易出錯?我覺得主要是下面幾個原因:
另外,C++的編譯對標準C++的實現各異,支持地也千差萬別,所以會有一些比較奇怪的問題,但是如果你一般用用C++的封裝,繼承,多態,以及namespace,const, refernece,
inline, templete, overloap, autoptr,還有一些OO 模式,並不會出現奇怪的問題。
而對於STL中的各種坑,我覺得是程序員們還對GP(泛型編程)理解得還不夠,STL是泛型編程的頂級實踐!屬於是大師級的作品,一般人很難理解。必需承認STL寫出來的代碼和編譯錯誤的確相當複雜晦澀,太難懂了。這也是C++的一個詬病。
這和 —— “C++是一門很恐怖的語言,而比它更恐怖的是很多不合格的程序員在使用着它”。注意我飄紅了“很多不合格的程序員”!
我覺得C++並不適合初級程序員使用,C++只適合高級程序員使用(參看《》和《》),正如《》中說的,C++適合那些對開發維護效率和系統性能同時關注的高級程序員使用。
這就好像飛機一樣,開飛機很難,開飛機要注意的東西太多太多,對駕駛員的要求很高,但你不能說飛機這個工具很爛,開飛機的坑太多。(註:我這裡並不是說C++是飛機,C是汽車,C++和C的差距,比飛機到汽車的差距少太多太多,這裡主要是類比,我們對待C++語言的心態!)
理解C++設計的最佳讀本是《》,在這本書中Stroustrup說了些事:
1)Stroustrup對C是非常欣賞,實際上早期C++許多的工作是對於C的強化和凈化,並把完全兼容C作為強制性要求。C89、C99中許多的改進正是從C++中所引進。可見,Stroustrup對C語言的貢獻非常之大。今天不管你對C++怎麼看,C++的確擴展和進化了C,對C造成了深遠的影響。
2)Stroustrup對於C的抱怨主要來源於兩個方面——在C++兼容C的過程中遇到了不少設計實現上的麻煩;以及守舊的K&R C程序員對Stroustrup的批評。很多人說C++的惡夢就是要去兼容於C,這並不無道理(Java就乾的比C++徹底得多),但這並不是Stroustrup考慮的,Stroustrup一邊在使盡渾身解數來兼容C,另一方面在拚命地優化C。
3)Stroustrup在書中直接說,C++最大的競爭對手正是C,他的目的就是——C能做到的,C++也必須做到,而且要做的更好。大家覺得是不是做到了?有多少做到了,有多少還沒有做到?
4)對於同時關注的運行效率和開發效率的程序員,Stroustrup多次強調C++的目標是——“在保證效率與C語言相當的情況下,加強程序的組織性;能保證同樣功能的程序,C++更短小”,這正是淺封裝的核心思想。而不是過渡設計的OO。(參看:)
5)這本書中舉了很多例子來回應那些批評C++有運行性能問題的人。C++在其第二個版本中,引入了虛函數機制,這是C++效率最大的瓶頸了,但我個人認為虛函數就是多了一次加法運算,但讓我們的代碼能有更好的組織,極大增加了程序的閱讀和降底了維護成本。(註:Lippman的《》也說明了C++不比C的程序在運行性能低。Bruce的《》也說C++和C的性能相差只有5%)
6)這本書中還講了一些C++的痛苦的取捨,印象最深的就是多重繼承,提出,拿掉,再被提出,反覆很多次,大家在得與失中不斷地辯論和取捨。這個過程讓我最大的收穫是——a) 對於任何一種設計都有好有壞,都只能偏重一方,b) 完全否定式的批評是不好的心態,好的心態應該是建設性地批評。
我對C++的感情
我先說說我學C++的經歷。
我畢業時,是直接從C跳過C++學Java的,但是學Java的時候,不知道為什麼Java要設計成這樣,只好回頭看C++,結果學C++的時候又有很多不懂,又只得回頭看C,最後發現,C -& C++ -& Java的過程,就是C++填C的坑,Java填C++的坑的過程。
注,下面這些東西可以看到Java在填C/C++坑:
當然時代還在前進,這個演變的過程還在C#和Go上體現着。不過我學習了C -& C++
-& Java這個填坑演進的過程,讓我明白了很多東西:
我從這個學習過程中得到的最大的收穫不是語言本身,而是各式各樣的編程技術和方法,和技術的演進的過程,這比語言本身更重要!(在這個角度上學習,你看到的不是一個又一個的坑,你看到的是——各式各樣讓你可以爬得更高的梯子)
我對C++的感情有三個過程:先是喜歡地要死,然後是恨地要死,現在的又愛又恨,愛的是這個語言,恨的是很多不合格的人在濫用和凌辱它。
C++語言發展大概可以分為三個階段():
在《》中說了 ,性能主要就是要省電,省電就是省錢,在數據中心還不明顯,在手機上就更明顯了,這就是為什麼Android 支持C++的原因。所以,在NB的電池或是能源出現之前,如果你需要注重程序的運行性能和開發效率,並更關注程序的運性能,那麼,應該首選
C++。這就是iOS開發也支持C++的原因。
今天的C++11中不但有更多更不錯的東西,而且,還填了更多原來C++的坑。(參看:,)
最後,非常感謝能和“@簡悅雲風”,“@淘寶諸霸”,“@Laruence”一起討論這個問題!無論你們的觀點怎麼樣,我都和你們“在一起”,嘿嘿嘿……
(轉載本站文章請註明作者和出處
,請勿用於任何商業用途)
【上篇】【下篇】C++ 及标准库中的那些大坑 - 文章 - 伯乐在线
& C++ 及标准库中的那些大坑
1. 变量初始化
这是使用 C++11 codecvt 时遇到的一个坑,转换编码时,mbstate_t 这个中间状态变量,必须初始化为0,否则运行出错,即:
// 不可以!
// 这样可以
mbstate mbst = {0};
// 这样也行
mbstate mbst = mbstate_t();
// 不可以!mbstate_t mbst;// 这样可以mbstate mbst = {0};// 这样也行mbstate mbst = mbstate_t();
这是第一个坑,并不算太坑,还比较容易调试和发现,也怪自己大意了。
经验:C++中的变量一定要初始化后再使用。
2. 匿名 std::thread 对象
这个坑要和 boost 进行比较,在 boost 中,是可以创建匿名 thread 对象的,并且这样的匿名对象跟 future、promise是可以正常配合使用的(《Boost标准库完全开发指南》一书中的示例代码就是这样写的)。
但是,在 C++ 标准库中不能这么干,会出现莫名其妙的错误,调试时也不会显示任何有价值的信息,最终确定这个问题真是费了我好大劲,因为根本没想到会是这个问题,毕竟 boost 里都正常使用了。
经验:尽量不使用匿名对象,如果想要用完立即释放,可以使用单独的代码块包裹。
3. 线程局部存储(TLS)
这是一个坑了我一天的大坑。
C++11 中,新引入了 thread_local 存储类型,等同于之前的 __declspec(thread),由于其具有真正的可移植性,所以我就尝试使用了,但这也是噩梦的开始。
我有一段代码,如果编译为 exe,在 xp 系统上能正常运行,但如果编译为 dll,在 xp 上运行就出错。由于 xp 上不能安装 VS 这种高科技玩意,只能用 x32_dbg 凑合调试,发现是空指针异常,指针来源为 fs:[2c],这是 TLS 指针啊,然后百度,找到了微软的文档 https://msdn.microsoft.com/en-us/library/y5f6w579 :
On XP systems, thread_local may not function correctly if a DLL uses thread_local data and it is loaded dynamically via LoadLibrary.
是的,如果 dll 中使用了 thread_local,这个 dll 将不能在 xp 上通过 LoadLibrary 动态加载。
解决办法也是有的:
既然不能通过 LoadLibrary 动态加载,那我静态加载不就行了,只要在编译 exe 时静态链接 dll,即 dll 在 exe 的导入表中,那就可以正常运行(这也要求 exe 必须是自己可编译的)
在 DllMain 中使用 TLS 相关的 API 手动初始化
祈祷 xp 早日完蛋
经验:或许我应该抛弃 xp 了。
4. dll 中的静态对象
这个坑跟上个坑是同时出现的,只是我当时用了静态链接的方式后,就运行正常了,也就没在意。直到后来又想在 C# 中调用 dll,这回没办法静态链接了。为了先实现功能,我选择了暂时删除 thread_local,但是在 xp 上依然运行出错,错误原因跟之前一样!卧槽,我特么明明都删掉了 thread_local 呀,为何还这样!!
又经过2个小时的调试,最终确定问题出在 C++17 标准库中的 std::experimental::filesystem::exists() 函数,但是经过我单步调试发现,这个函数并没有使用 TLS,只用到了一些全局静态对象,莫非是全局静态对象的问题?
于是还是找文档吧,跟上个问题同一个网址 https://msdn.microsoft.com/en-us/library/y5f6w579 :
Starting in C++11, a static local variable initialization is guaranteed to be thread-safe. This feature is sometimes called magic statics. However, in a multithreaded application all subsequent assignments must be synchronized. The thread-safe statics feature can be disabled by using the /Zc:threadSafeInit- flag to avoid taking a dependency on the CRT.
在 C++11 中,静态变量的初始化是线程安全的,这个所谓的“线程安全”,就是引入了 TLS 来进行一些额外的检查,好在这个特性是可以禁用的,编译时添加 /Zc:threadSafeInit- 选项即可(注意最后的减号),禁用后就不会使用 TLS 了,也就可以在 xp 上动态加载了。
经验:xp 去死吧!去死吧!去死吧!
注:这些问题在 VS2015 Update 2 中发现,应该也会持续存在于之后的 VS 版本中。你用C++踩过多少坑?编程业界大牛带你了解真正的C++【c++吧】_百度贴吧
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&签到排名:今日本吧第个签到,本吧因你更精彩,明天继续来努力!
本吧签到人数:0成为超级会员,使用一键签到本月漏签0次!成为超级会员,赠送8张补签卡连续签到:天&&累计签到:天超级会员单次开通12个月以上,赠送连续签到卡3张
关注:304,065贴子:
你用C++踩过多少坑?编程业界大牛带你了解真正的C++收藏
分享之前我还是要推荐下我自己的C/C++学习交流裙:三 四 三 八 九 一 三 六 五,不管你是小白还是大牛,小编我都挺欢迎,不定期分享干货,包括我自己整理的一份2017最新的C/C++资料和零基础入门教程,送给大家,欢迎初学和进阶中的小伙伴C++的初衷 1)早期C++许多的工作是对于C的强化和净化,并把完全兼容C作为强制性要求。C89、C99中许多的改进正是从C++中所引进。可见,Stroustrup对C语言的贡献非常之大。今天不管你对C++怎么看,C++的确扩展和进化了C,对C造成了深远的影响。
c++,博为峰软件培训免费试听,0元入学,签署就业保障协议,不就业不收费!博为峰,真正的为年轻人服务的企业,14年间培训15万余名软件工程师,成绩斐然!
  2)在C++兼容C的过程中遇到了不少设计实现上的麻烦;以及守旧的K&R C程序员对Stroustrup的批评。很多人说C++的恶梦恶梦就是要去兼容于C,这并不无道理(Java就干的比C++彻底得多),但这并不是Stroustrup考虑的,Stroustrup一边在使尽浑身解数来兼容C,另一方面在拼命地优化C。
  3)C++最大的竞争对手正是C,他的目的就是——C能做到的,C++也必须做到,而且要做的更好。大家觉得是不是做到了?有多少做到了,有多少还没有做到?  4)对于同时关注的运行效率和开发效率的程序员,Stroustrup多次强调C++的目标是——“在保证效率与C语言相当的情况下,加强程序的组织性;能保证同样功能的程序,C++更短小”,这正是浅封装的核心思想。而不是过渡设计的OO。(参看:面向对象是个骗局)
  5)《C++演化和设计》这本书中举了很多例子来回应那些批评C++有运行性能问题的人。C++在其第二个版本中,引入了虚函数机制,这是C++效率最大的瓶颈了,但我个人认为虚函数就是多了一次加法运算,但让我们的代码能有更好的组织,极大增加了程序的阅读和降底了维护成本。(注:Lippman的《深入探索C++对象模型》也说明了C++不比C的程序在运行性能低。Bruce的《Think in C++》也说C++和C的性能相差只有5%)
 6)这本书中还讲了一些C++的痛苦的取舍,印象最深的就是多重继承,提出,拿掉,再被提出,反复很多次,大家在得与失中不断地辩论和取舍。这个过程让我最大的收获是——a) 对于任何一种设计都有好有坏,都只能偏重一方,b) 完全否定式的批评是不好的心态,好的心态应该是建设性地批评。
 C++有多少坑  个人觉得C++的坑其实并不多,因为C++太灵活了,想怎么搞就怎么搞,所以,各种不经意地滥用和乱搞。某些人C和C++都没学好,大多数人用C++写C,所以,C的坑和C++的坑合并了。如果你能花两到三周的时候读一下《Effecitve C++》里的那50多个条款,你就知道C++里的坑并不多,而且,有很多条款告诉我们C++是怎么解决C的坑的。然后,你可以读读《Exceptional C++》和《More Exceptional C++》,你可以了解一下C++各种问题的解决方法和一些常见的经典错误。
  C++在解决了很多C语的坑的同时,也因为OO和泛型又引入了一些坑。消一些,加一些,我个人感觉上总体上只比C多10%左右吧。但是你有了开发速度更快,代码更易读,更易维护的500%的利益。  C++的编译对标准C++的实现各异,支持地也千差万别,所以会有一些比较奇怪的问题,但是如果你一般用用C++的封装,继承,多态,以及namespace,const, refernece, inline, templete, overloap, autoptr,还有一些OO 模式,并不会出现奇怪的问题。  而对于STL中的各种坑,我觉得是程序员们还对GP(泛型编程)理解得还不够,STL是泛型编程的顶级实践!属于是大师级的作品,一般人很难理解。必需承认STL写出来的代码和编译错误的确相当复杂晦涩,太难懂了。这也是C++的一个诟病。
C++是一门很恐怖的语言,而比它更恐怖的是很多不合格的程序员在使用着它。  很多编程大牛对C++的感情波动是:先是喜欢地要死,然后是恨地要死,最是的又爱又恨,爱的是这个语言,恨的是很多不合格的人在滥用和凌辱它。
 C++的未来  C++语言发展大概可以分为三个阶段:  第一阶段:  这一阶段C++语言基本上是传统类型上的面向对象语言,并且凭借著接近C语言的效率,在工业界使用的开发语言中占据了相当大份额;
  第二阶段:  这一阶段由于标准模板库(STL)和后来的Boost等程式库的出现,泛型程式设计在C++中占据了越来越多的比重性。当然,同时由于Java、C#等语言的出现和硬件价格的大规模下降,C++受到了一定的冲击;
第三阶段:  以Loki、MPL等程式库为代表的产生式编程和模板元编程的出现,C++出现了发展历史上又一个新的高峰,这些新技术的出现以及和原有技术的融合,使C++已经成为当今主流程式设计语言中最复杂的一员。  在《Why C++? 王者归来》中说了 ,性能主要就是要省电,省电就是省钱,在数据中心还不明显,在手机上就更明显了,这就是为什么Android 支持C++的原因。所以,在NB的电池或是能源出现之前,如果你需要注重程序的运行性能和开发效率,并更关注程序的运性能,那么,应该首选 C++。这就是iOS开发也支持C++的原因。  今天的C++11中不但有更多更不错的东西,而且,还填了更多原来C++的坑。
总结  C++并不完美,但学C++必然让你受益无穷。是那些不合格的、想对编程速成的程序员让C++变得坑多。
+裙学习领取资料,交流学习
登录百度帐号《C++之那些年踩过的坑(二)》
我的图书馆
《C++之那些年踩过的坑(二)》
作者:刘俊延(Alinshans)出处:http://www.cnblogs.com/GodA/p/6554591.html本系列文章针对我在写C++代码的过程中,尤其是做自己的项目时,踩过的各种坑。以此作为给自己的警惕。今天讲一个小点,虽然小,但如果没有真正理解它,没有真正熟悉它的里里外外,是很容易出错的 —— inline。关于一些简单的介绍和使用,可以先看我 这篇笔记。接下来进入正题。一、如何使用 inline?你知道,inline 函数可以减小函数调用的开销,你可能会想,嗯,我这个函数那么短,我把它声明为 inline,可以提高程序运行的效率!考虑这样一个例子:// A.h// A.cc// main.cc
#include "A.h"
}首先,你知道,①inline 需要看到函数实体,所以要跟定义放在一起。于是你想在 A.cc 中在为 foo 的定义加上一个 inline :inline void A::foo(int i)然后开心的编译运行,WTF!!!编译器居然报错了?!!不就加了个 inline 吗!仔细观察编译器给的出错信息,如果你用的是VS,那么你大概会看到这样的信息: error LNK2019: 无法解析的外部符号……如果你用的是GCC,你会发现当你使用g++ -c main.cc
时(即编译),是不会产生任何错误的,然后当你使用g++ main.o -o a.out
时(即链接),就报错了。说明,这是链接的时候出错了。在这里要说明一下,大多数的建置环境都是在编译过程进行 inlining(为了替换函数调用,编译器需要知道函数的实体长什么样,这解释了①),某些可以在连接期完成,少数的可以在运行期完成。我们只考虑绝大部分情况:Inlining 在大多数C++程序中是编译期行为。大部分函数默认的就是外部链接,也就是外部可以访问,而 inline 函数默认具有内部链接,也就是对本文件可见,对其它文件不可见。那么自然我们在 main.cc 中调用它,没法看到它的定义,于是就出现了连接错误。OK,你学到了 ②一般 inline 需要放在头文件中。那 bar 函数确实是定义在头文件中了,这下你想声明为 inline 总没问题了吧!确实,这没有问题,不过,它如你所想提高效率了吗?我们可以探究一下。在vs下可以用调试看反汇编,现在用GCC分别运行以下命令:g++ -E main.cc -o main.i
g++ -S main.i -o main.s
g++ -O2 -S main.i -o main2.s
我们来看一下 main.s 中的主要部分:
-9(%ebp), %eax
$1, (%esp)
%eax, %ecx
__ZN1A3fooEi
-9(%ebp), %eax
$1, (%esp)
%eax, %ecx
__ZN1A3barEi
-4(%ebp), %ecx我们再看一下 main2.s 中的这个部分:
-9(%ebp), %ecx
$1, (%esp)
__ZN1A3fooEi
$2, 4(%esp)
$LC0, (%esp)
-4(%ebp), %ecx在不开优化的情况下,程序诚实的执行,开O2优化的情况下,我们已经看不到 bar 函数的调用了。不过这真的是拜你加的 inline 所赐的吗?为了验证,我们去掉 inline,再次重复上面的过程,然后你就会发现,重新生成的两份汇编代码还是 一模一样 。OK,或许你早就知道这个了,想知道更多,具体可以看http://en.cppreference.com/w/cpp/language/inline里面说的,定义在类内的函数,是自动 inline 的,LLVM CodingStandards也告诉我们:③不要给定义在类中的函数使用 inline。你可能已经急坏头了,这里不给用,那里不给用,行行行,头文件是吧,不在类内是吧,我在类外!于是你改成了这样的代码(虽然很少人这样写,但万一呢?还别说我就干过= =!):// A.h嗯,这没问题。把这份代码用刚才的方法,得到的汇编代码还是一样的。若你现在想尝试把 inline 去掉,看看会有什么变化,你经过刚刚的事情,大概会预测,应该还是会得到相同的汇编代码吧!编译器应该会帮你去优化!如果你不想尝试,好的,我帮你。WTF!!!编译器又报错了??发生了什么??二、什么时候应该使用 inline?嗯,终于我们来到了第二个问题,我们发现,在这种情况下,去掉 inline ,居然无法通过编译了!它给出来的错误信息是:重定义的符号。让我们冷静下来,想一想,然后你就会恍然大悟:一个函数可以有多次声明,但只能有一次定义,而我们定义在 A.h 的 bar 函数的定义,被 A.cc 和 main.cc 都包含了一遍!所以就出现了重定义的错误!Ok,类成员函数我们也许不会这样写,要么放到类内,要么放到源文件中,当然模板类的情况我们稍后再谈。不过对于普通的函数,我们就很容易忽略了。例如,我们想在 A.h 中加一个函数:// A.h它很短,要不要使用 inline 呢?经过刚刚的问题,你应该会谨慎的想到,这里,要使用 inline ,不然就会出错。非常好,你的想法是对的。原因跟上面提到的是一样的。当然,它不是非得使用 inline 不可,使用 static 事实上也可以解决这个问题,在这里只是为了说明 inline 可以使用的情况。但当你用上模板时,情况发生了改变。若你把这个 max 函数改成一个模板函数:template &typename T&
T max(const T& a, const T& b)
return a & b ? b :
}这个时候,无论你有没有使用 inline,它都是可以运行的。这是因为,模板是具有“内联”语义的。所以,类模板,函数模板,类函数模板,都不需要加 inline 。回到正题,什么时候可以使用 inline 呢?④使用 inline 当且仅当函数的定义在头文件中并且被多个源文件包含时。三、 inline 的真正意义?现在你该好好的思考,什么是 inline,是内联吗?inline 的意义是什么,是发起一个内联请求吗?你认为加了 inline 能提高程序的运行效率,但是并不会有这样的变化。有的时候,你不加 inline,却会出错,这是为什么呢?好好的想想。inline,跟 static , extern 一样,都是链接指令,它在很久很久以前,是作为给编译器优化的提示符。而 inline 的含义是非绑定的,编译器可以自由的选择、决定是否 inline 一个函数。如今,编译器根本不需要这样的提示,如果它认为一个函数值得 inline,它会自动 inline,否则,即使你 inline 了,它也会拒绝。如果你仔细阅读 http://en.cppreference.com/w/cpp/language/inline的话,你会知道,有很多地方,都会隐式 inline 。在这篇SO中,有一段话: It is said that inline static extern inline看完你应该差不多能理解了。现在的编译器,并不需要你用 inline 提醒,所以 ⑤当且仅当你认为使用 inline 会加快程序运行效率时,不要使用 inline 。inline 这个关键字,在C++里就是一个骗局。它真正的意义并不是去内联一个函数,而是表示别怕!无论你看到了多少个定义,但实体就我一个! 在Reference中有有这样一句话: Because the meaning of the keyword inline for functions came to mean "multiple definitions are permitted" rather than "inlining is preferred", that meaning was extended to variables.翻译过来就是 ⑥ inline 的含义是 “允许多重定义” 而不是 “去内联函数” 。全文基于 C++17 及以前的讨论。四、总结1、inline 需要看到函数实体,所以要跟定义放在一起2、一般 inline 需要放在头文件中3、不要给定义在类中的函数使用 inline 4、使用 inline 当且仅当函数的定义在头文件中并且被多个源文件包含时5、当且仅当你认为使用 inline 会加快程序运行效率时,不要使用 inline6、inline 的含义是 “允许多重定义” 而不是 “去内联函数”7、模板不需要声明 inline,也具有 inline 的语义※注:以上总结建立在不熟悉、不够了解 inline 的情况下。若在使用 inline 的时候,很明白很清楚在做什么,会发生什么,那怎么用都可以啦,有时候用了就是好看!!
喜欢该文的人也喜欢下次自动登录
现在的位置:
& 综合 & 正文
C++的坑真的多吗?
本文原创版权归
所有,转载请详细标明作者及出处,严禁用于任何商业用途!
先说明一下,我不希望本文变成语言争论贴。希望下面的文章能让我们客观理性地了解C++这个语言。(另,我觉得技术争论不要停留在非黑即白的二元价值观上,这样争论无非就是比谁的嗓门大,比哪一方的观点强,毫无价值。我们应该多看看技术是怎么演进的,怎么取舍的。)
周五的时候,我在我的微博上发了一个贴说了一下一个网友给我发来的C++程序的规范和内存管理写的不是很好(后来我删除了,因为当事人要求),我并非批判,只是想说明其实程序员是需要一些“疫苗”的,并以此想开一个“程序员疫苗的网站”,结果,@简悦云风同学:“不要用
C++ 直接用 C , 就没那么多坑了。”就把这个事带入了语言之争。
: 说C++比C的坑更多的人我可以理解,但理性地思考一下。C语言的坑也不少啊,如果说C语言有90个坑,那么C++就是100个坑(另,我看很多人都把C语言上的坑也归到了C++上来),但是C++你得到的东西更多,封装,多态,继承扩展,泛型编程,智能指针,……,你得到了500%东西,但却只多了10%的坑,多值啊。
结果引来了更多的回复(只节选了一些言论):
@淘宝褚霸:“自从5年前果断扔掉C++,改用了ansi c后,我的生活质量大大提升,没有各种坑坑我。”
@Laruence说: “我确实用不到, C语言灵活运用struct, 可以很好的满足这些需求.//@左耳朵耗子: 封装,继承,多态,模板,智能指针,这也用不到?这也学院派?//@Laruence: 问题是, 这些东西我都用不到… C语言是工程师搞的, C++是学院派搞的”
那么,C++的坑真的多么?我还请大家理性地思考一下。
C++真的比C差吗?
我们先来看一个图——《》,从这个图上看,C程序员比C++的程序员在注释中使用fuck的字眼多一倍。这说明了什么?我个人觉得这说明C程序员没有C++程序员淡定。
不要太纠结上图,只是轻松一下,我没那么无聊,让我们来看点真正的论据。
相信用过C++的程序员知道,C++的很多特性主要就是解决C语言中的各种不完美和缺陷:(注:C89、C99中许多的改进正是从C++中所引进的)
用namespace解决了很C函数重名的问题。
用const/inline/template代替了宏,解决了C语言中宏的各种坑。
用const的类型解决了很多C语言中变量值莫名改变的问题。
用引用代替指针,解决了C语言中指针的各种坑。这个在Java里得到彻底地体现。
用强类型检查和四种转型,解决了C语言中乱转型的各种坑。
用封装(构造,析构,拷贝构造,赋值重载)解决了C语言中各种复制一个结构体(struct)或是一个数据结构(link, hashtable, list, array等)中浅拷贝的内存问题的各种坑。
用封装让你可以在成员变量加入getter/setter,而不会像C一样只有文件级的封装。
用函数重载、函数默认参数,解决了C中扩展一个函数搞出来像func2()之类的ugly的东西。
用继承多态和RTTI解决了C中乱转struct指针和使用函数指针的诸多让代码ugly的问题。
用RAII,智能指针的方式,解决了C语言中因为出现需要释放资源的那些非常ugly的代码的问题。
用OO和GP解决各种C语言中用函数指针,对指针乱转型,及一大砣if-else搞出来的ugly的泛型。
用STL解决了C语言中和数据结构的N多种坑。
(注意:上面我没有提重载运算符和异常,前者写出来的代码并不易读和易维护(参看《》后面的那个示例),坑也多,后者并不成熟(相对于Java的异常),但是我们需要知道try-catch这种方式比传统的不断地判断函数返回值和errno形成的大量的if-else在代码可读性上要好很多)
上述的这些东西填了不知有多少的C语言编程和维护的坑。少用指针,多用引用,试试autoptr,用用封装,继承,多态和函数重载…… 你面对的坑只会比C少,不会多。
C++的坑有多少?
C++的坑真的不多,如果你能花两到三周的时候读一下《》里的那50多个条款,你就知道C++里的坑并不多,而且,有很多条款告诉我们C++是怎么解决C的坑的。然后,你可以读读《》和《》,你可以了解一下C++各种问题的解决方法和一些常见的经典错误。
当然,C++在解决了很多C语的坑的同时,也因为OO和泛型又引入了一些坑。消一些,加一些,我个人感觉上总体上只比C多10%左右吧。但是你有了开发速度更快,代码更易读,更易维护的500%的利益。
另外,不可否认的是,C++中的代码出了错误,有时候很难搞,而且似乎用C++的人会觉得C++更容易出错?我觉得主要是下面几个原因:
C和C++都没学好,大多数人用C++写C,所以,C的坑和C++的坑合并了。
C++太灵活了,想怎么搞就怎么搞,所以,各种不经意地滥用和乱搞。
另外,C++的编译对标准C++的实现各异,支持地也千差万别,所以会有一些比较奇怪的问题,但是如果你一般用用C++的封装,继承,多态,以及namespace,const, refernece,
inline, templete, overloap, autoptr,还有一些OO 模式,并不会出现奇怪的问题。
而对于STL中的各种坑,我觉得是程序员们还对GP(泛型编程)理解得还不够,STL是泛型编程的顶级实践!属于是大师级的作品,一般人很难理解。必需承认STL写出来的代码和编译错误的确相当复杂晦涩,太难懂了。这也是C++的一个诟病。
这和 —— “C++是一门很恐怖的语言,而比它更恐怖的是很多不合格的程序员在使用着它”。注意我飘红了“很多不合格的程序员”!
我觉得C++并不适合初级程序员使用,C++只适合高级程序员使用(参看《》和《》),正如《》中说的,C++适合那些对开发维护效率和系统性能同时关注的高级程序员使用。
这就好像飞机一样,开飞机很难,开飞机要注意的东西太多太多,对驾驶员的要求很高,但你不能说飞机这个工具很烂,开飞机的坑太多。(注:我这里并不是说C++是飞机,C是汽车,C++和C的差距,比飞机到汽车的差距少太多太多,这里主要是类比,我们对待C++语言的心态!)
理解C++设计的最佳读本是《》,在这本书中Stroustrup说了些事:
1)Stroustrup对C是非常欣赏,实际上早期C++许多的工作是对于C的强化和净化,并把完全兼容C作为强制性要求。C89、C99中许多的改进正是从C++中所引进。可见,Stroustrup对C语言的贡献非常之大。今天不管你对C++怎么看,C++的确扩展和进化了C,对C造成了深远的影响。
2)Stroustrup对于C的抱怨主要来源于两个方面——在C++兼容C的过程中遇到了不少设计实现上的麻烦;以及守旧的K&R C程序员对Stroustrup的批评。很多人说C++的恶梦就是要去兼容于C,这并不无道理(Java就干的比C++彻底得多),但这并不是Stroustrup考虑的,Stroustrup一边在使尽浑身解数来兼容C,另一方面在拼命地优化C。
3)Stroustrup在书中直接说,C++最大的竞争对手正是C,他的目的就是——C能做到的,C++也必须做到,而且要做的更好。大家觉得是不是做到了?有多少做到了,有多少还没有做到?
4)对于同时关注的运行效率和开发效率的程序员,Stroustrup多次强调C++的目标是——“在保证效率与C语言相当的情况下,加强程序的组织性;能保证同样功能的程序,C++更短小”,这正是浅封装的核心思想。而不是过渡设计的OO。(参看:)
5)这本书中举了很多例子来回应那些批评C++有运行性能问题的人。C++在其第二个版本中,引入了虚函数机制,这是C++效率最大的瓶颈了,但我个人认为虚函数就是多了一次加法运算,但让我们的代码能有更好的组织,极大增加了程序的阅读和降底了维护成本。(注:Lippman的《》也说明了C++不比C的程序在运行性能低。Bruce的《》也说C++和C的性能相差只有5%)
6)这本书中还讲了一些C++的痛苦的取舍,印象最深的就是多重继承,提出,拿掉,再被提出,反复很多次,大家在得与失中不断地辩论和取舍。这个过程让我最大的收获是——a)对于任何一种设计都有好有坏,都只能偏重一方,b)完全否定式的批评是不好的心态,好的心态应该是建设性地批评。
我对C++的感情
我先说说我学C++的经历。
我毕业时,是直接从C跳过C++学Java的,但是学Java的时候,不知道为什么Java要设计成这样,只好回头看C++,结果学C++的时候又有很多不懂,又只得回头看C,最后发现,C -& C++ -& Java的过程,就是C++填C的坑,Java填C++的坑的过程。
注,下面这些东西可以看到Java在填C/C++坑:
Java彻底废弃了指针(指针这个东西,绝对让这个社会有几百亿的损失),使用引用。
Java用GC解决了C++的各种内存问题的诟病,当然也带来了GC的问题,不过功大于过。
Java对异常的支持比C++更严格,让编程更方便了。
Java没有像C++那样的template/macro/函数对象/操作符重载,泛型太晦涩,用OO更容易一些。
Java改进了C++的构造、析构、拷贝构造、赋值。
Java对完全抛弃了C/C++这种面向过程的编程方式,并废弃了多重继承,更OO(如:用接口来代替多重继承)
Java比较彻底地解决了C/C++自称多年的跨平台技术。
Java的反射机制把这个语言提升了一个高度,在这个上面可以构建各种高级用法。
C/C++没有一些比较好的类库,比如UI,线程 ,I/O,字符串处理等。(C++0x补充了一些)
当然时代还在前进,这个演变的过程还在C#和Go上体现着。不过我学习了C -& C++
-& Java这个填坑演进的过程,让我明白了很多东西:
我明白了OO是怎么一回事,重要的是明白了OO的封装,继承,和多态是怎么实现的。(参看我以前写过的《》和《》)
我明白了STL的泛型编程和Java的各种花哨的技术是怎么一回事,以及那些很花哨的编程方法和技术。
我明白了C,C++,Java的各中坑,这就好像玩火一样,我知道怎么玩火不会烧身了。
我从这个学习过程中得到的最大的收获不是语言本身,而是各式各样的编程技术和方法,和技术的演进的过程,这比语言本身更重要!(在这个角度上学习,你看到的不是一个又一个的坑,你看到的是——各式各样让你可以爬得更高的梯子)
我对C++的感情有三个过程:先是喜欢地要死,然后是恨地要死,现在的又爱又恨,爱的是这个语言,恨的是很多不合格的人在滥用和凌辱它。
C++语言发展大概可以分为三个阶段():
第一阶段从80年代到1995年。这一阶段C++语言基本上是传统类型上的面向对象语言,并且凭借著接近C语言的效率,在工业界使用的开发语言中占据了相当大份额;
第二阶段从1995年到2000年,这一阶段由于标准模板库(STL)和后来的Boost等程式库的出现,泛型程式设计在C++中占据了越来越多的比重性。当然,同时由于Java、C#等语言的出现和硬件价格的大规模下降,C++受到了一定的冲击;
第三阶段从2000年至今,由于以Loki、MPL等程式库为代表的产生式编程和模板元编程的出现,C++出现了发展历史上又一个新的高峰,这些新技术的出现以及和原有技术的融合,使C++已经成为当今主流程式设计语言中最复杂的一员。
在《》中说了 ,性能主要就是要省电,省电就是省钱,在数据中心还不明显,在手机上就更明显了,这就是为什么Android 支持C++的原因。所以,在NB的电池或是能源出现之前,如果你需要注重程序的运行性能和开发效率,并更关注程序的运性能,那么,应该首选
C++。这就是iOS开发也支持C++的原因。
今天的C++11中不但有更多更不错的东西,而且,还填了更多原来C++的坑。(参看:,)
C++并不完美,但学C++必然让你受益无穷。
是那些不合格的、想对编程速成的程序员让C++变得坑多。
最后,非常感谢能和“@简悦云风”,“@淘宝诸霸”,“@Laruence”一起讨论这个问题!无论你们的观点怎么样,我都和你们“在一起”,嘿嘿嘿……
(转载本站文章请注明作者和出处
,请勿用于任何商业用途)
【上篇】【下篇】}

我要回帖

更多关于 成真恋爱学是坑人的吗 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信