陳胖胖與cppreference 中文


本站AI自動(dòng)判斷提供您所需要的app下載:點(diǎn)我下載安裝,你懂的APP

正文:

近日,日本核污水排海事件引發(fā)了廣泛的輿論關(guān)注。在這一背景下,核輻射檢測(cè)儀成為了各大電商平臺(tái)上的暢銷爆款。然而,網(wǎng)購的輻射檢測(cè)儀到底靠譜嗎?最近,上海的徐女士向記者反映了一個(gè)令人震驚的事件。

據(jù)徐女士介紹,8月29日晚,她在家中蒸了一鍋大閘蟹,并使用了一個(gè)月前在電商平臺(tái)上購買的核輻射檢測(cè)儀進(jìn)行檢測(cè)。然而,結(jié)果卻令她和家人感到十分恐慌,警報(bào)聲不斷響起,頻繁警示劑量超標(biāo)。情急之下,他們四處報(bào)警,并將家里的海鮮全部扔掉。更諷刺的是,第二天,徐女士又將檢測(cè)儀對(duì)準(zhǔn)女兒的肚皮,結(jié)果警報(bào)聲再次響起,女兒當(dāng)場(chǎng)驚恐萬分,甩開檢測(cè)儀就捂著肚子跑了。

這一事件引發(fā)了徐女士的懷疑,她開始質(zhì)疑這款號(hào)稱精準(zhǔn)的核輻射檢測(cè)儀是否名不副實(shí)。于是,她與線上客服進(jìn)行了交涉。徐女士表示,在使用這個(gè)檢測(cè)儀后,家里不敢再食用海鮮,不得不扔掉了大量食物。雖然這樣的損失在某種程度上還算較小,但這個(gè)問題的嚴(yán)重性不容忽視。

對(duì)于網(wǎng)購的核輻射檢測(cè)儀是否靠譜,我們需要認(rèn)真思考。雖然電商平臺(tái)上的產(chǎn)品多種多樣,但并不代表所有產(chǎn)品都能夠達(dá)到所宣傳的精準(zhǔn)檢測(cè)效果。在購買這類產(chǎn)品時(shí),消費(fèi)者應(yīng)該保持警惕,選擇有信譽(yù)的品牌和商家。

此外,政府監(jiān)管部門也有責(zé)任加強(qiáng)對(duì)這類產(chǎn)品的質(zhì)量監(jiān)管,確保消費(fèi)者的權(quán)益不受侵害。只有通過嚴(yán)格的檢測(cè)和監(jiān)管,才能讓消費(fèi)者放心購買并使用核輻射檢測(cè)儀。

總之,核輻射檢測(cè)儀的真實(shí)性和可靠性是一個(gè)需要引起關(guān)注的問題。消費(fèi)者在購買這類產(chǎn)品時(shí)應(yīng)保持警惕,同時(shí)政府部門也應(yīng)加強(qiáng)監(jiān)管力度,確保市場(chǎng)上的產(chǎn)品質(zhì)量和安全性。只有這樣,才能讓人們安心使用這樣的產(chǎn)品,保障自身的健康與安全。

高強(qiáng)度使用了一年的C++,又適逢讀完了Scott Meyers的《Effective Modern C++》,很想寫下這篇文章,說說我對(duì)C++的看法。

從Java到C++

最初接觸C++是在大一夏季學(xué)期,三周時(shí)間入門“C++面向?qū)ο缶幊獭薄km然C語言學(xué)得不錯(cuò),但這門課還是讓我頭暈?zāi)垦#恢啤<葲]理解面向?qū)ο蟮乃枷耄矝]看懂C++稀奇古怪的語法。為數(shù)不多的印象就是可以用cout替代printf,用引用替代指針,僅此而已。

后來,我與C++漸行漸遠(yuǎn),因?yàn)橛辛肆硪粋€(gè)美好的邂逅——Java。我對(duì)Java是一見鐘情,簡潔的語法,純粹的面向?qū)ο筇匦裕S富的類庫,廣泛的應(yīng)用領(lǐng)域,都讓我愛不釋手。與之相比,C++為了兼容C而作的妥協(xié),前后不一的設(shè)計(jì)理念,奇怪的標(biāo)準(zhǔn)庫,越來越詭異的新特性,實(shí)在讓人難以接受。

若干年后,成為研究生的我由于算法研究的需要,不得不重視C++。起初,拷貝和賦值的概念讓我迷惑了好一會(huì)兒。我無法理解為什么C++設(shè)計(jì)這么細(xì)致的控制手段,值傳遞和引用傳遞,拷貝構(gòu)造和移動(dòng)構(gòu)造,左值和右值等等等等。這些在Java中都是不存在的,所有對(duì)象都是引用類型就好了,為什么要有那么多規(guī)則?

今天,我可以肯定地說出答案:為了性能

一切為了性能

有人說,C++是個(gè)全能語言,底層、算法、軟件、后端都能做。但都能做就意味著都做得不好,專業(yè)性和通用性不可兼得。C++開發(fā)軟件、做Web后端顯然是不合適的,沒有大量的類庫支撐,開發(fā)過程困難重重。而C++在算法領(lǐng)域則是一騎絕塵,SLAM、深度學(xué)習(xí),都得用C++寫,即便你用了TensorFlow的Python API,它內(nèi)部也是用C++實(shí)現(xiàn)的。之所以如此,是因?yàn)樗惴ㄍǔJ怯?jì)算密集型應(yīng)用,好的實(shí)現(xiàn)和差的實(shí)現(xiàn)在性能上千差萬別,算法研究者必須用一種能夠精確管控性能的編程語言,這種語言就是C++。

很多人以為C++比Java快是因?yàn)镴ava必須運(yùn)行在虛擬機(jī)上,沒有C++編譯成機(jī)器碼來得直接。這種觀念不是沒有道理,但不免淺薄。現(xiàn)在的Java有了JIT(Just-in-time compilation,即時(shí)編譯)優(yōu)化,性能已經(jīng)越來越接近C++,所以性能差距并不體現(xiàn)在虛擬機(jī)上。

C++和Java,以及其它高級(jí)語言,如C#、Python的真正區(qū)別是,只有C++支持手動(dòng)管理內(nèi)存并指定數(shù)據(jù)傳遞方式。所謂手動(dòng)管理內(nèi)存,不僅僅是通過指針訪問特定地址的內(nèi)存數(shù)據(jù),更在于由用戶決定對(duì)象分配在棧上還是堆上,這一點(diǎn)至關(guān)重要。Java的對(duì)象全部分配在堆上,而我們知道堆內(nèi)存是不連續(xù)的,所以Java程序需要頻繁訪問不連續(xù)的內(nèi)存,這會(huì)破壞緩存的有效性,從而導(dǎo)致性能下降。再來看數(shù)據(jù)傳遞方式,Java所有的變量都是引用,變量賦值和參數(shù)傳遞都是對(duì)引用的拷貝,這在一定程度上避免了初級(jí)C++程序員容易犯的冗余拷貝的問題,但同時(shí)也扼殺了靈活性,畢竟有時(shí)候,你真的需要拷貝對(duì)象,而不僅僅是傳遞引用。

不過,C++提供的靈活性并不一定能轉(zhuǎn)化為高性能,糟糕的C++代碼可能比同樣功能的Java代碼速度更慢,因?yàn)槟愫苋菀紫萑霟o謂的拷貝和構(gòu)造中去。作為C++程序員,我們無疑要十分清楚每行代碼背后的隱藏含義,這也注定了寫好C++并不容易。

寫作本文的動(dòng)機(jī)源于《Effective Modern C++》這本書,與幾年前讀《Effective Java》的感受完全不同,C++這部似乎每頁都寫滿了兩個(gè)字——“性能”。作者對(duì)性能的挖掘已經(jīng)到了匪夷所思的地步,一絲一毫都不放過。下面我們就談?wù)劇癈++程序員的自我修養(yǎng)”,探討C++編程中需要注意的地方。

C++程序員的自我修養(yǎng)

內(nèi)存管理

直接聲明對(duì)象還是用new創(chuàng)建對(duì)象?前者在棧上申請(qǐng)空間,后者在堆上申請(qǐng)空間。處理器對(duì)棧內(nèi)存的操作一般快于堆內(nèi)存,除非真的必須動(dòng)態(tài)管理內(nèi)存,建議都在棧上聲明對(duì)象,這也可以避免忘記delete導(dǎo)致的內(nèi)存泄露。

數(shù)據(jù)傳遞方式

首先,一定要熟悉C++11提供的五種拷貝控制函數(shù),包括拷貝構(gòu)造函數(shù)、移動(dòng)構(gòu)造函數(shù)、拷貝賦值運(yùn)算符、移動(dòng)賦值運(yùn)算符、析構(gòu)函數(shù)。它們都是隱式調(diào)用的,首要任務(wù)就是弄清楚它們各自被調(diào)用的場(chǎng)合,然后是它們的功能。給定一個(gè)賦值語句或函數(shù)調(diào)用語句,要準(zhǔn)確地分析出這句代碼調(diào)用了哪種拷貝控制函數(shù)。這期間,需要理解右值和右值引用,否則移動(dòng)構(gòu)造和移動(dòng)賦值就無從談起。

有了這些基礎(chǔ)后,我們才能針對(duì)特定的場(chǎng)景,選擇最合適的函數(shù)參數(shù)。比如,addName函數(shù)用來把參數(shù)存入names_容器,從下面三種方式中選擇最合理的方式:

// Version 1, by-value copy name to the function. void addName(std::string name) { names_.push_back(std::move(name)); } // Version 2, provide two overloads, one with const reference, another with rvalue reference. void addName(const std::string& name) { names_.push_back(name); } void addName(std::string&& name) { names_.push_back(std::move(name)); } // Version 3, use universal reference to integrate lvalue reference and rvalue reference. template<typename T> void addName(T&& name) { names_.push_back(std::forward<T>(name)); }

這里沒有最優(yōu)解,版本2和版本3最節(jié)約性能,但可能會(huì)導(dǎo)致源碼或二進(jìn)制碼體積較大,版本1最簡潔,但會(huì)多出一個(gè)移動(dòng)構(gòu)造。取舍的關(guān)鍵在于具體的應(yīng)用場(chǎng)景,假如對(duì)象的移動(dòng)構(gòu)造非常廉價(jià),完全不耗費(fèi)性能,那就選擇版本1,否則就選擇版本2或3。

說到這里,便又涉及到另一個(gè)問題,你是否清楚對(duì)象拷貝構(gòu)造和移動(dòng)構(gòu)造的代價(jià)?對(duì)于自定義類,如果沒有手動(dòng)聲明這些構(gòu)造函數(shù)和賦值運(yùn)算符,編譯器會(huì)為我們自動(dòng)生成。但編譯器自動(dòng)生成的這些函數(shù)長什么樣?如果我們需要自定義這些函數(shù),應(yīng)該遵循什么規(guī)則?答案很繁瑣,但我們必須逐一理清。

了解標(biāo)準(zhǔn)庫

了解標(biāo)準(zhǔn)庫不只是會(huì)用,而是理解其內(nèi)部實(shí)現(xiàn)原理。比如,vector內(nèi)部是數(shù)組、list內(nèi)部是雙向鏈表、unordered_map是哈希表、map是紅黑樹等等。當(dāng)然,基本的數(shù)據(jù)結(jié)構(gòu)常識(shí)是必不可少的,以免頻繁插入數(shù)組,或是頻繁通過下標(biāo)訪問鏈表元素的情況發(fā)生。此外,vector、unordered_map、map如何動(dòng)態(tài)擴(kuò)容,是否需要預(yù)先分配空間,何時(shí)使用emplace_back代替push_back,如何區(qū)分push_back的兩種重載,這些細(xì)節(jié)問題都是值得我們注意的。

向C++11、C++14、C++17靠攏

從C++11開始,標(biāo)準(zhǔn)委員會(huì)每3年會(huì)頒布新的標(biāo)準(zhǔn),絕不拖延。目前的最新版是C++17,明年會(huì)頒布C++20。現(xiàn)在已經(jīng)是2019年了,可很多人還在用C++98寫代碼。要知道,Ubuntu 16.04默認(rèn)的gcc版本是5.4,該版本已經(jīng)支持了C++14的全部特性。所以,大部分開發(fā)者使用的電腦上應(yīng)該至少可以利用到C++14的新特性。

從現(xiàn)在開始,動(dòng)起來吧,了解auto關(guān)鍵字,使用nullptr替代NULL,使用using替代typedef,嘗試使用constexpr和noexcept關(guān)鍵字,使用lambda替代std::bind,使用std::thread或std::async替代pthread。這些改變會(huì)使代碼變得更清晰、更高效。

切記,避免過早優(yōu)化

著名計(jì)算機(jī)科學(xué)家Donald Knuth說過一句名言:“過早優(yōu)化是萬惡之源。”初時(shí)不解,現(xiàn)在深有體會(huì)。程序的擴(kuò)展性依賴于早期的合理設(shè)計(jì),但不意味著我們應(yīng)該在剛開始就設(shè)計(jì)出一套復(fù)雜的系統(tǒng)。軟件工程中的敏捷開發(fā)正是針對(duì)這一問題給出的解決方案,快速迭代,隨時(shí)重構(gòu),才能開發(fā)出良好的軟件。我們不是神仙,沒人能預(yù)知今后的變化,為不存在的需求設(shè)計(jì)復(fù)雜的架構(gòu),是一種愚蠢的做法。

之所以要強(qiáng)調(diào)這點(diǎn),是因?yàn)檫^去犯的此種錯(cuò)誤太多。看過好的軟件架構(gòu),就想用到自己的項(xiàng)目中來,殊不知?jiǎng)e人也是經(jīng)過反復(fù)迭代才到了今天的地步,絕不是一開始就成型的。一個(gè)軟件,只有完美貼合其所處的應(yīng)用場(chǎng)景,才是好的設(shè)計(jì),生搬硬套設(shè)計(jì)模式是沒用的。

結(jié)語

最后,本文的目的其實(shí)是引導(dǎo)大家去學(xué)習(xí)真正的C++知識(shí),不是看我們這些博客,而是學(xué)習(xí)一手資料。

我推薦《C++ Primer》、《Effective Modern C++》和cppreference,第一本書學(xué)習(xí)C++基礎(chǔ),第二本進(jìn)階,第三個(gè)是C++官方文檔,用來平時(shí)檢索。

喜歡編程的小伙伴,請(qǐng)加Q群:521763391

News concept: Breaking News On Screen in grunge

作者:王金戈

鏈接:
https://zhuanlan.zhihu.com/p/82895086

來源:知乎著作權(quán)歸作者所有。

商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。