陳胖胖與cppreference 中文


本站AI自動判斷提供您所需要的app下載:點我下載安裝,你懂的APP

正文:

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

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

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

對于網購的核輻射檢測儀是否靠譜,我們需要認真思考。雖然電商平臺上的產品多種多樣,但并不代表所有產品都能夠達到所宣傳的精準檢測效果。在購買這類產品時,消費者應該保持警惕,選擇有信譽的品牌和商家。

此外,政府監管部門也有責任加強對這類產品的質量監管,確保消費者的權益不受侵害。只有通過嚴格的檢測和監管,才能讓消費者放心購買并使用核輻射檢測儀。

總之,核輻射檢測儀的真實性和可靠性是一個需要引起關注的問題。消費者在購買這類產品時應保持警惕,同時政府部門也應加強監管力度,確保市場上的產品質量和安全性。只有這樣,才能讓人們安心使用這樣的產品,保障自身的健康與安全。

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

從Java到C++

最初接觸C++是在大一夏季學期,三周時間入門“C++面向對象編程”。雖然C語言學得不錯,但這門課還是讓我頭暈目眩,不知所云。既沒理解面向對象的思想,也沒看懂C++稀奇古怪的語法。為數不多的印象就是可以用cout替代printf,用引用替代指針,僅此而已。

后來,我與C++漸行漸遠,因為有了另一個美好的邂逅——Java。我對Java是一見鐘情,簡潔的語法,純粹的面向對象特性,豐富的類庫,廣泛的應用領域,都讓我愛不釋手。與之相比,C++為了兼容C而作的妥協,前后不一的設計理念,奇怪的標準庫,越來越詭異的新特性,實在讓人難以接受。

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

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

一切為了性能

有人說,C++是個全能語言,底層、算法、軟件、后端都能做。但都能做就意味著都做得不好,專業性和通用性不可兼得。C++開發軟件、做Web后端顯然是不合適的,沒有大量的類庫支撐,開發過程困難重重。而C++在算法領域則是一騎絕塵,SLAM、深度學習,都得用C++寫,即便你用了TensorFlow的Python API,它內部也是用C++實現的。之所以如此,是因為算法通常是計算密集型應用,好的實現和差的實現在性能上千差萬別,算法研究者必須用一種能夠精確管控性能的編程語言,這種語言就是C++。

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

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

不過,C++提供的靈活性并不一定能轉化為高性能,糟糕的C++代碼可能比同樣功能的Java代碼速度更慢,因為你很容易陷入無謂的拷貝和構造中去。作為C++程序員,我們無疑要十分清楚每行代碼背后的隱藏含義,這也注定了寫好C++并不容易。

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

C++程序員的自我修養

內存管理

直接聲明對象還是用new創建對象?前者在棧上申請空間,后者在堆上申請空間。處理器對棧內存的操作一般快于堆內存,除非真的必須動態管理內存,建議都在棧上聲明對象,這也可以避免忘記delete導致的內存泄露。

數據傳遞方式

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

有了這些基礎后,我們才能針對特定的場景,選擇最合適的函數參數。比如,addName函數用來把參數存入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)); }

這里沒有最優解,版本2和版本3最節約性能,但可能會導致源碼或二進制碼體積較大,版本1最簡潔,但會多出一個移動構造。取舍的關鍵在于具體的應用場景,假如對象的移動構造非常廉價,完全不耗費性能,那就選擇版本1,否則就選擇版本2或3。

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

了解標準庫

了解標準庫不只是會用,而是理解其內部實現原理。比如,vector內部是數組、list內部是雙向鏈表、unordered_map是哈希表、map是紅黑樹等等。當然,基本的數據結構常識是必不可少的,以免頻繁插入數組,或是頻繁通過下標訪問鏈表元素的情況發生。此外,vector、unordered_map、map如何動態擴容,是否需要預先分配空間,何時使用emplace_back代替push_back,如何區分push_back的兩種重載,這些細節問題都是值得我們注意的。

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

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

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

切記,避免過早優化

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

之所以要強調這點,是因為過去犯的此種錯誤太多。看過好的軟件架構,就想用到自己的項目中來,殊不知別人也是經過反復迭代才到了今天的地步,絕不是一開始就成型的。一個軟件,只有完美貼合其所處的應用場景,才是好的設計,生搬硬套設計模式是沒用的。

結語

最后,本文的目的其實是引導大家去學習真正的C++知識,不是看我們這些博客,而是學習一手資料。

我推薦《C++ Primer》、《Effective Modern C++》和cppreference,第一本書學習C++基礎,第二本進階,第三個是C++官方文檔,用來平時檢索。

喜歡編程的小伙伴,請加Q群:521763391

News concept: Breaking News On Screen in grunge

作者:王金戈

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

來源:知乎著作權歸作者所有。

商業轉載請聯系作者獲得授權,非商業轉載請注明出處。