論壇上看到的有人提出的關於C++的一些問題,真是細緻(下面還有回覆說他對C++只知其一;不知其二的),一直覺得本身的C++仍是能夠的,但是看了以後真是內牛滿面,爲何本身歷來沒有想的這麼深刻,差距真的有這麼大嗎?淚奔~,之後不再敢說本身會C++了。html
=== 數據類型 ===
1. 你知道bool類型的變量佔一個字節,可是殊不知道bool類型在內存裏是如何存儲的。true是0嗎?false是1嗎?
固然,你能夠說,程序員不該該關心它在內存裏如何存儲。但是,C++卻恰恰容許你使用union類型以及對指針進行類型轉換,讓你偷看到它的表示。
但是,即便如此,你保證不一樣的編譯器都用一樣的方式表示true和false嗎?
2. const char*(指向字符常量的指針變量)和char*(指向字符變量的指針變量)是兩個不一樣的類型。若是一個函數接受char*類型參數,那麼若是給它傳入const char*型參數,編譯器會警告。可是這真的有必要嗎?若是一個函數只是在某些狀況下要修改參數中指針指向的數組呢?或者它還要將參數再傳給別的函數呢?
3. 你知道char, short, int, long, long long分別佔幾個字節嗎?你知道42,42L,42LL分別是什麼類型嗎?它們在32位x86和64位x86_64機器上分別佔幾個字節嗎?你怎麼寫一個整數常量,保證它是64位的?(這是個人同窗遇到的真實問題)
4. int a; a=9; if (a=42); { cout<<"a="<<a<<endl; }爲何老是輸出"a=42"?C++明明有bool類型,可是爲何這樣的代碼竟然編譯通得過?
=== 運算符 ===
5. 看代碼:
java
1
2
3
|
string place, item;
cout<<
"There is a "
<<item<<
" in "
<<place<<endl;
cout<<
"在"
<<place<<
"裏面有一個"
<<item<<endl;
|
以上代碼沒法國際化,由於幾個字句的順序依賴於語言的語法。C++不讓你寫一個串,對全部語言都使用。仍是Java的MessageFormat好
python
1
2
3
4
|
String englishPattern =
"There is a {0} in {1}."
;
String chinesePattern =
"在{1}裏面有一個{0}。"
;
MessageFormat.format(englishPattern,
"Windows"
,
"IE"
);
// There is a IE in Windows
MessageFormat.format(chinesePattern,
"Windows"
,
"IE"
);
// 在Windows裏面有一個IE
|
6. 你知道算數運算符+ - * / %以及位運算符<< >> & | ^ ~以及關係運算符== != < > <= >=誰的優先級高嗎?
表達式(3+1<<2)的值是幾?我猜你會猜錯。寫個代碼試試。
若是我想判斷a和b異或的結果是否等於0,這樣寫(a^b==0)對嗎?
7. 筆試的時候常常有人問你「i = i++ + i++ + i++」以後i的值等於幾,可是你卻沒有拒絕這個公司的勇氣。
=== 函數 ===
8. 以下代碼
mysql
1
2
3
4
|
template
<
class
T>
void
swap(T &a, T &b) {
T tmp=a; a=b; b=tmp;
}
|
這段代碼必須放在頭文件裏,只要一改,全部用到它的代碼都要從新編譯。並且編譯階段會把每一個用到的數據類型編譯一份上述代碼,若是每一個.cpp文件裏都用到某個共同類型,那麼編譯器也會給每一個.cpp都編譯一份這個函數,連接時還無法優化。
=== 面向對象的編程 ===
9. 以下代碼: ios
1
2
|
string a =
"foo"
;
string b = a +
"bar"
+ a +
"baz"
;
|
你說不清楚以上代碼究竟建立了多少個string對象。
10. 給你一個指針SomeClass* ptr;,你怎麼知道ptr指向的對象是SomeClass呢,仍是它的子類呢?或者這麼問:給你一個指針,你怎麼知道一個指針void *ptr;指向的目標是否是SomeClass呢?只需編程求解「是」仍是「不是」。
答案是:SomeClass *ptr2 = dynamic_cast<SomeClass*>(ptr);。若是ptr真的指向SomeClass,那麼它返回這個指針自己;若是不是,則返回NULL。
什麼?沒人告訴過你?但是Java裏有「instanceof」這個運算符,專門用來檢測對象的類型的。
11. private繼承:你的兒子動不了你的東西,可是你的朋友(friend)能夠。這是豈有此理?
12. 菱形繼承:
class A { int x; };
class B1 : public A;
class B2 : public A;
class C : public B1, public B2;
那麼C裏面有幾份x?
若是我建立一個對象C c;那麼c.x是哪一個x?
13. 下面的代碼爲何能編譯經過?明明f的參數類型錯了。
c++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
#include <iostream>
using
namespace
std;
class
Blah {
public
:
Blah(
int
x) : _x(x) {}
void
speak() {
cout<<
"x="
<<_x<<endl;
}
private
:
int
_x;
};
void
f(Blah b) {
b.speak();
}
int
main() {
f(42);
return
0;
}
|
14. 爲何類的私有成員變量要寫在頭文件裏?那還叫「私有」嗎?類的結構變了,即便只改變私有成員變量,全部依賴於這個類的代碼都要從新編譯。舊的代碼若是不從新編譯,就不兼容新的。
若是都是個人代碼還好,萬一我提供的是一個很流行的很經常使用的庫,豈不是個人類的結構都不敢改了?一改,全部的用戶都要從新編譯?
解法:請使用私有實現模式(private implementation,簡稱pimpl模式)
15. 深拷貝。你能夠把對象賦給另外一個變量,能夠按值傳入一個函數,也能夠按值從一個函數返回。可是其中要經歷大量的拷貝。
=== 異常處理 ===
16. 怎樣寫一段代碼,使得即便出現異常的時候也會被執行?好比,若是打開了一個文件,如何在異常出現的狀況下,先把文件關掉,再把異常拋到塊或者函數外面?
答案是把這樣的代碼寫在一個對象的析構函數中,並在棧上分配這個對象。C++中只有try-catch,卻沒有finally語句。惟一可以在異常拋出的時候執行的代碼就是析構函數了。
可是,就算是析構函數,也不必定會執行。C++中,異常一旦拋出,運行環境就會從棧頂到棧底,一個幀一個幀地尋找一個能抓住這個異常的catch語句。惋惜,若是這樣的catch語句不存在,那麼結果是程序當即終止。任何析構函數都不會執行。
17. 編譯器處理C和C++混合的代碼很是痛苦,由於C的函數根本不知道「異常」這個概念,可是C++編譯器必需要處理「一個C++函數調用了一個C函數,這個C函數又調用了一個C++函數……」這樣的狀況,同時C編譯又會容許「一個C函數調用了一個C++函數(用extern "C"提供了接口),這個C++函數又調用了一個C函數……」這樣的狀況,而C++也必須考慮到這種狀況。
=== 編譯 ===
18. 編譯一個C++程序很是慢,一般花上半個小時編譯一個不大的程序都是常有的事。我據說過有一個軟件須要沒日沒夜地編譯5天才能編譯好。
若是想體驗一下的話,就開始學習wxWidget吧。即便是隻有一個對話框HelloWorld程序,也要用半分鐘編譯。
其緣由通常是頭文件會互相引用,可是由於使用了大量的模板等,每次都要從新處理一堆頭文件。並且構建的時候,make程序有時候須要用半分鐘的時間才僅僅能知道「哪些文件變了,哪些文件須要從新編譯」,更不用說真的編譯的時間了。
19. 若是想提升速度,卻是能夠考慮「預編譯頭文件」。VC6.0就支持這個功能。GCC也有。就是把一個頭文件做爲「預編譯頭」,先用編譯器預處理。編譯器會緩存預處理的結果。它對你的代碼的要求,就是全部的.cpp文件都必須首先引用這個頭文件(不然頭文件之間會互相影響)。
可是預編譯頭文件會生成一個幾十MB的緩存。固然,磁盤空間多了,也不用擔憂它。可是一不當心把它連代碼拷貝走了就很差了。就怕你不知道VisualC++生成的xxxxxx.pch是幹什麼用的。(PCH=Pre-Compiled Header)
這也是爲何不推薦一開始就使用IDE的緣由。若是你一開始就用VC,你大概不知道這個stdafx.h爲何這麼特殊,沒了它編譯就不經過;把別的include預編譯語句放到#include "stdafx.h"以前,也編譯通不過,也不知道它究竟是幹什麼用的,也不知道如何把它從工程裏去掉。另外一個沒有它的工程編譯得慢,你殊不知道怎麼把它加到工程裏。估計通常教材不會提它。其實它就是那個「預編譯頭文件」。
=== 二進制接口 ===
20. C++容許你進行函數名重載。你能夠寫幾十個函數,都叫同一個名字,好比都叫foo。
但是,當你把這幾十個foo編譯起來,放到一個動態連接庫bar.so(或者bar.dll)裏,你用另外一個程序打開bar.so,想從裏面取出一個foo函數來調用。可是……糟糕!這麼多函數都叫foo!!!怎麼知道我要哪一個foo呢??
實際上C++會把名字進行「混淆」(英文叫mangling),也就是把函數改個名字,把參數的類型以及返回值的類型編入函數中。好比GCC會把上述那個void f(Blah)函數命名爲「_Z1f4Blah」。
糟糕的是,不一樣的編譯器,混淆的方法不太同樣。因此,一個程序用編譯器A編譯,另外一個程序用編譯器B編譯,那麼它們就不能互相調用對方的函數。
想一想爲何Qt庫的Windows版要分mingw和MSVC(Microsoft Visual C++)兩個版本?
21. 不一樣的編譯器對於「異常處理」不太一致。
22. 剛纔說過了類的「私有成員」問題,類一改,就都不兼容了,都要從新編譯
=== 一些雜事 ===
23. 凡是涉及「模板」的代碼,程序編譯出錯之後,輸出的信息都很是難以看懂。
=== 缺失的特性 ===
有些東西原本應該寫入編程語言中,由於其實現高度依賴編譯器,但這些卻不是C++的一部分,好比:
- coroutine:相似很輕很輕的線程。它們不能同時執行。一個coroutine必須暫停本身,把控制權交給另外一個coroutine,另外一個適當的時候跳回來。頗有用的結構,適合於「生產者-消費者」模型,也適合於大規模的並行處理,也能夠簡化不少算法(好比二叉樹遍歷)。惋惜能實現這個的只是一篇論文介紹的技術,今年(2013)才發表:http://ulir.ul.ie/handle/10344/2927
- 垃圾回收:正式的名字是「自動內存管理」。用於防止「無用單元」(分配出去了內存,但全部指向它的指針都不見了,再也沒法訪問它)和「懸垂引用」(一個指針,本來指向一個有效的對象,但這個對象的內存被回收了,這個指針變成了一個無效的指針)。Boost庫中有「智能指針」(smart pointer),但那是一個極其樸素的引用計數實現,一旦產生循環引用就完蛋了。並且,Boost裏有好幾種不一樣的「智能指針」,很難判斷到底應該用哪種指針。此外還有基於tracing(非引用技術)的Boehm GC,但那是一個保守的垃圾回收,它不能識別全部的指針(由於內存裏都是數字,不知道哪一個變量是值,哪一個是指針),因此也不能回收全部的垃圾。另外,程序員都假設對象一旦分配了是不會移動的,因此也不能用「移動式的垃圾回收」,難以免內存碎片的產生。(lighttpd啊,你死得好慘啊!!!!你說不是你的錯,是malloc的錯,說malloc不整理內存碎片,明明有不少空閒內存,就是分配不出來啊!有木有!!可你怎麼不說你是用C寫的啊!!!C程序員有責任管理內存啊!!!!有木有!!!有木有啊!!!!內存木有了你找誰去喊冤啊!!!!!!!)
--git
後面的回覆:程序員
1.語言是對機器的抽象,類型就是抽象方法的一種,抽象的目的就是爲了隱藏下層細節,後面好幾個point都是這個問題。你真的須要知道bool的內存表示麼。拿python來講,你不須要一個數字背後的內存表示是32位整型仍是一個高精度數,這對你是透明的。你能在java裏獲得一個變量的內存地址麼?不過,若是你想寫編譯器,那麼ISO C++ 98標準4.7節明確的寫了"If the source type is bool, the value false is converted to zero and the value true is converted to one."。
2.const是一個接口約定,你說本身是const,那麼就表明任何狀況下都不會修改引用目標。c++ const的問題幾乎和java的checked exception同樣,必定程度上保證了程序的安全性和正確性,可是帶來了接口的複雜。一種手段解決了某些問題,同時確定會帶來新的問題。
3.這個問題和問題1同樣,須要補充的是,在一些加密或者網絡傳輸的長期下,須要用到指定長度的整型類型以優化性能,c99加入了Standard Integer Types,提供了int8_t,uint8_t,int16_t,uint16_t,int32_t,uint32_t,int64_t,uint64_t。
4.c++的bool類型是一種整型,任何整型能夠隱式轉換成bool,這固然不必定是好的feature,這麼作主要是爲了保持對c的源代碼兼容性。
5.messageformat這條徹底沒噴到點子上,java的設計和sprintf幾乎同樣,就c++算沒有等價面向對象的實現,在類庫層面實現一個也不困難。java真正的優點在於標準字符串是unicode編碼,而c/c++是ansi。
6~9徹底是扯犢子了
7. 從我畢業那年開始我從沒見過這樣的筆試題。若是真有相似的問題,他想要的絕對不是一個編譯器實現決定的準確數值,而是題目裏的兩個基礎知識點:符號貪婪匹配和表達式求值順序。我把題目改爲int n=0; n=n++;你能回答上來麼。
後面關於頭文件、二進制兼容性的幾點是說到點子上了。c++不少設計上缺陷的根源都來自一點——保持對c的代碼兼容性。這個特性語言發展的初期,吸引了大量的開發者,可是這個沉重的歷史包袱隨着語言的發展愈來愈凸顯。
語言是對機器的抽象,使得某些複雜的機器特性對使用者透明;另外一方面也是對問題的抽象,將現實世界的問題抽象成類型系統、控制流。 另一點是,透明不是絕對的,這取決於你關注的方面。
好比gc,不管是用引用計數仍是根搜索實現,本質上是對內存模型的抽象,從物理硬件的線性地址模型抽象到徹底對使用者透明。在大部分業務開發的代碼裏,gc以必定的機器性能換取了人的開發效率。因此說白了gc是一種程序時間和人月的trade off,在不一樣的場景下,根據資源約束條件的不一樣來取捨。百度、Google的搜索前臺server,QQ的通訊server,絕對只可能會用c/c++,由於海量請求、低時延的要求下,性能是很是critical的,同理mysql、memcached、redis、mongodb等等關注單機性能的存儲組件無一不是用c/c++,他們不會用任何gc。而對於處理業務邏輯的應用服務器,如今已經基本看不到c和c++的影子了。
--github
- coroutine:相似很輕很輕的線程。它們不能同時執行。一個coroutine必須暫停本身,把控制權交給另外一個coroutine,另外一個適當的時候跳回來。頗有用的結構,適合於「生產者-消費者」模型,也適合於大規模的並行處理,也能夠簡化不少算法(好比二叉樹遍歷)。惋惜能實現這個的只是一篇論文介紹的技術,今年(2013)才發表:http://ulir.ul.ie/handle/10344/2927
--協程boost有,並且分爲stackless(參考boost::asio)和stackfull(目前比較流行的golang也是這種),看你想怎麼用了
- 垃圾回收:正式的名字是「自動內存管理」。用於防止「無用單元」(分配出去了內存,但全部指向它的指針都不見了,再也沒法訪問它)和「懸垂引用」(一個指針,本來指向一個有效的對象,但這個對象的內存被回收了,這個指針變成了一個無效的指針)。Boost庫中有「智能指針」(smart pointer),但那是一個極其樸素的引用計數實現,一旦產生循環引用就完蛋了。並且,Boost裏有好幾種不一樣的「智能指針」,很難判斷到底應該用哪種指針。此外還有基於tracing(非引用技術)的Boehm GC,但那是一個保守的垃圾回收,它不能識別全部的指針(由於內存裏都是數字,不知道哪一個變量是值,哪一個是指針),因此也不能回收全部的垃圾。另外,程序員都假設對象一旦分配了是不會移動的,因此也不能用「移動式的垃圾回收」,難以免內存碎片的產生。(lighttpd啊,你死得好慘啊!!!!你說不是你的錯,是malloc的錯,說malloc不整理內存碎片,明明有不少空閒內存,就是分配不出來啊!有木有!!可你怎麼不說你是用C寫的啊!!!C程序員有責任管理內存啊!!!!有木有!!!有木有啊!!!!內存木有了你找誰去喊冤啊!!!!!!!)
--個人理解GC僅僅是內存回收的一種捷徑,c++裏面包括不少資源類型,好比文件描述符,鎖之類的,並且java貌似只是從必定程度上解決了內存問題,可是引用計數這種輔助的東西,對於資源管理仍是不能不用。
http://microcai.org/2013/07/27/gc-is-wrong-way-of-doing-memory-managment.html,我以爲這個比較中肯
golang
——
http://microcai.org/2013/07/27/gc-is-wrong-way-of-doing-memory-managment.html,我以爲這個比較中肯
RAII確實是C++的風格。是處理資源的合理方法。
不過,除了內存之外的其它資源的管理,如文件、網絡鏈接、鎖等,確實不該該靠垃圾回收來管理。垃圾回收也不是爲管理這些資源設計的。(注意「引用計數」只是一種垃圾回收的方法,更多的靈活的方法是「跟蹤」:從「根對象」掃描整個堆,並扔掉沒有觸及的對象)
對於Java來講,等效於RAII的是try-finally,而不是垃圾回收。資源在try中獲取,而finally保證try執行以後必定會執行(無論是正確仍是錯誤的狀況)。finally和C++中棧上對象的析構函數是相似的,但即便沒有任何catch能抓住異常,它也會執行。
Python 2.5中增長了with語句。在with塊結束的時候,它綁定的資源必定會被處理。如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
# 舊寫法(危險,但很流行)。依賴於引用計數。
# 若是Python的實現不用引用計數(Jython、PyPy等)就會形成資源泄漏。
txt
=
open
(
"somefile.txt"
).read()
# 舊寫法(安全),保證f會關掉
f
=
open
(
"somefile.txt"
)
try
:
txt
=
f.read()
finally
:
f.close()
# 新寫法:保證f.__exit__()必定會執行(它會調用f.close())。
with
open
(
"somefile.txt"
) as f:
txt
=
f.read()
|
Java 1.7中增長了try-with-resource結構,更增強化了這種「將資源綁定在靜態的做用域上」的概念(C++也是用棧上對象的做用域與資源綁定,這一點是相通的)。
1
2
3
4
5
6
7
8
9
10
11
12
|
// 舊寫法
BufferedReader br =
new
BufferedReader(
new
FileReader(path));
try
{
return
br.readLine();
}
finally
{
if
(br !=
null
) br.close();
}
// 新寫法
try
(BufferedReader br =
new
BufferedReader(
new
FileReader(path))) {
return
br.readLine();
}
|
Ruby自己對塊支持得很好。通常這種資源能夠用函數將其限制在傳入的回調塊中。
1
2
3
|
File
:
:open
(
"somefile.txt"
)
do
|f|
puts f.read
end
|
甚至對鎖也能夠這樣作。Ruby文檔裏的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
require
'thread'
semaphore = Mutex.
new
a =
Thread
.
new
{
semaphore.synchronize {
# access shared resource
}
}
b =
Thread
.
new
{
semaphore.synchronize {
# access shared resource
}
}
|
Scala有scala-arm庫作自動資源管理。實際上只是用Ruby的風格封裝了一下Java的文件等資源。https://github.com/jsuereth/scala-arm
但不得不說,老Python風格的用垃圾回收來自動關閉文件,是不正確的。(我猜測microcai反對的也正是這種用法,而不是垃圾回收自己)起碼這種作法將語言限定在「非延遲的引用計數」這種垃圾回收策略上,而這種策略的性能不好。PHP也是由於不少程序依賴這種策略,以致於使用了其它垃圾回收方法就會致使程序不正確。Facebook的HipHop虛擬機(高性能的PHP虛擬機)實現起來很吃力,也是由於這個。
固然,能夠將內存也做爲「資源」的一種,用一樣的方法,經過將動態的資源綁定到靜態的資源上,以保證安全地分配和回收。可是,我我的認爲內存具備本身的特殊性,因此適合使用垃圾回收,而不是手動地管理。
- 內存的「回收」並無時效性。若是文件不及時關閉,磁盤上的文件多是不完整的,其餘進程可能看不到已經寫入的數據。鎖不及時釋放,會阻塞其它線程,甚至形成死鎖。網絡鏈接若是不及時關閉,對端會認爲你沒有傳輸完。可是內存若是不及時回收,最嚴重的後果不過是一個進程暫時佔用了多於實際須要的內存,垃圾回收事後,內存就會恢復合理的佔用量。實際上,「回收」這個動做自己是要花時間的。若是一旦內存不用就必須當即回收,還要擠佔程序執行的時間。
- 在一個對象被複雜地共享的環境中,很難肯定對象生存期由哪一個對象維護。若是一個對象產生之後,傳遞給了其它模塊,並且是多個模塊,那麼任何單個模塊都沒法決定這個對象是否還要保留。
- 將內存管理的負擔交給程序員,輕則加劇程序員的負擔,重則增長受打擊面,容易引起更多的問題。在構造複雜的數據結構的狀況下,若是內存管理必須由程序員顯式地進行,那麼數據結構也會變得複雜。
例如:如在用引用計數和智能指針實現環形鏈表的時候,必須讓正向引用使用強引用,而反向引用使用弱引用,以免環形引用使得內存沒法釋放。這樣,程序員不但要考慮鏈表的結構正確性,還要考慮引用類型的正確性。並且必須考慮若是去掉其中一個環節就會致使下一個環節的強引用計數變爲0,而後致使以後一系列的對象要自動析構。儘管增長了複雜度,但是弱引用卻並非爲這種情形設計的:弱引用的代價比強引用更大。
總結一下,垃圾回收(正如它正式的名字叫「自動內存管理」同樣)是適合內存管理的機制,但並不適合管理全部的資源。不少支持垃圾回收的語言都有專門的機制(try-finally或者with結構)來實現更好的資源管理。
--
這是傳說中的喪心病狂嘛~
首先C++的原始設計方案就對於底層開發來講就是糟糕的~這也就致使了Bjarne Stroustrup等人在後續的更新設計上變得愈來愈保守和沒有自信。
從根本性上來講,C++ 0x的gc設計仍是語法糖。須要回收內存時,以掃描大面積內存的時間開銷代價來保證內存管理的儘量有效!
這種設計至少對於底層開發者來講是不能妥協的!而若是用於應用層開發,那爲何不選擇一種垃圾回收更爲優美、更容易書寫的語言呢?(好比Java、C#...雖然它們的問題也是一大坨)
這就是一個Check&Balance的過程了,也是C++標準制定者們頭疼到死的課題。
語言好壞的討論,老是停留在語言特性的層面上,剩下的看客拋出一句「各有各的用途」,實在是太沒養分了。
初學者剛一門語言的時候,每每注意力都集中在語法特性上,由於目標是寫出能運行的代碼。實際上細節的語法特性只是語言設計哲學的體現。現實世界裏,選擇編程語言時語法特性是一個很是次要的因素。
你們喜聞樂見的cpp和java,它們最主要的區別是什麼,是GC?多重繼承?bool類型?標準庫?編譯?異常處理?根本都不在點子上,他們最核心的區別是,Java跑在JVM這個虛擬機上,而cpp只依賴一個運行時,JVM這一層抽象是兩者各類差異的關鍵。另外一個例子是perl和python,python的哲學是"There should be one -- and preferably only one --- obvious way to do it", 而perl的則是"There's more than one way to do it."。換個角度,嘗試從語言的設計思路和哲學上來比較分析不一樣的語言,不要總糾結在語言特性上。
社區、歷史、演進和外圍工具也是其餘幾個能夠關注的方面。
--
: 有幾條確實是比較無厘頭,我認爲你是認真,因此我解釋下。
: 1.語言是對機器的抽象,類型就是抽象方法的一種,抽象的目的就是爲了隱藏下層細節,後面好幾個point都是這個問題。你真的須要知道bool的內存表示麼。拿python來講,你不須要一個數字背後的內存表示是32位整型仍是一個高精度數,這對你是透明的。你能在java裏獲得一個變量的內存地址麼?不過,若是你想寫編譯器,那麼ISO C++ 98標準4.7節明確的寫了"If the source type is bool, the value false is converted to zero and the value true is converted to one."。
嗯
: 2.const是一個接口約定,你說本身是const,那麼就表明任何狀況下都不會修改引用目標。c++ const的問題幾乎和java的checked exception同樣,必定程度上保證了程序的安全性和正確性,可是帶來了接口的複雜。一種手段解決了某些問題,同時確定會帶來新的問題。
說的在理。確實以爲和Checked Exception有一樣的問題。
: 3.這個問題和問題1同樣,須要補充的是,在一些加密或者網絡傳輸的長期下,須要用到指定長度的整型類型以優化性能,c99加入了Standard Integer Types,提供了int8_t,uint8_t,int16_t,uint16_t,int32_t,uint32_t,int64_t,uint64_t。
關鍵的是「常量」,也就是42, 42L, 42LL之間的區別。這些類型雖然規定了定長的數據類型,可是沒有定長數據類型的常量。有可能這位同窗的這個用途比較特殊,常量的類型也會決定程序的正確性。並且這個bug他調試了一天多才發現是使用了L而不是LL,使得在32位機上是32位,64位機上是64位。
: 5.messageformat這條徹底沒噴到點子上,java的設計和sprintf幾乎同樣,就c++算沒有等價面向對象的實現,在類庫層面實現一個也不困難。java真正的優點在於標準字符串是unicode編碼,而c/c++是ansi。
這個吐槽的是iostream庫,並非C++自己。
Qt中的QString是支持按位置替換的。QString("There is a %1 in %0").arg("Windows").arg("IE");
: 6~9徹底是扯犢子了
6~7是純粹黑,8和9是認真的。
8是關於模版。模版確實會形成代碼爆炸。
9若是是字符串,還比較輕鬆;若是是高精度整數,就要當心了。GNU-MP的C++綁定專門爲C++優化了,使得相似a=b+c這樣的表達式能夠用一個加法運算完成。
一旦涉及對象按值傳入函數和按值返回,就和第15條同樣了。
: int n=0; n=n++;你能回答上來麼。
也是未定義行爲(一個表達式裏兩個部分有反作用)。用gcc4.8編譯是1,用clang3.3編譯是0。
: 後面關於頭文件、二進制兼容性的幾點是說到點子上了。
嗯。這幾個是認真的,尤爲是二進制兼容性。
: 百度、Google的搜索前臺server,QQ的通訊server,絕對只可能會用c/c++,由於海量請求、低時延的要求下,性能是很是critical的…………他們不會用任何gc。
根本的決定因素仍是性能。我認爲之因此沒有選擇垃圾回收的語言,根本緣由是目前非垃圾回收的語言能夠比垃圾回收的語言跑得更快。可是若是在虛擬機上跑,Type Inference和JIT-Compiling能夠提供Ahead-of-time Compiling語言(如Fortran/Ada/C/C++/Rust/Go等)沒法觸及的優化機遇。固然這兩個是將來的技術(雖然上個世紀90年代早就有人研究過),也許暫時應用還比較少。
: 同理mysql、memcached、redis、mongodb等等關注單機性能的存儲組件無一不是用c/c++,他們不會用任何gc。
舉一個反例吧:CouchDB,也是以高性能著稱的數據庫,和mongodb相似。使用Erlang語言實現的,而Erlang是在BEAM虛擬機上跑的函數式語言,而函數式語言必須使用GC。
--