為什麼C++的STL被設計成值語義,而非更加常見的引用語義?知乎使用者By853B2020-05-19 11:08:08

感覺有兩點吧,第一是C++記憶體管理複雜,設計成引用語義萬一出現迴圈引用怎麼辦,它在設計之初並沒有GC(雖說現在也沒有,,,)出現這種情況不好解決。第二是C++有指標和引用,需要傳遞引用的時候可以用這兩者解決,所以設計成現在這個模式更加方便而且也不影響功能。

為什麼C++的STL被設計成值語義,而非更加常見的引用語義?裴浩2020-05-19 12:29:32

因為題主所說的“其他語言”基本上不需要使用者進行記憶體管理。

C++ 需要使用者掌控某種型別的物件是否允許多個物件共享一個,所以智慧指標才會有

unique_ptr

shared_ptr

兩種。一個物件被多個 owner 引用,GC 語言用慣了會覺得這就是個語言必備的 feature,無可厚非。但是呢,底層至少有個引用計數的加減問題,這個效能開銷 C++ 需要使用者顯式地註明:我知道這個開銷,我願意承擔。

比如,一個

vector

在可見範圍內,被多個函式共享,你可以傳遞引用。

void

g

std

::

vector

<

int

>&

v

{

/* 。。。 */

}

void

f

()

{

std

::

vector

<

int

>

v

{

1

2

3

};

// f 對 v 進行一些操作

g

v

);

// g 對 v 進行另一些操作

}

沒有任何額外開銷,只有引用的傳遞(指向

vector

的指標的複製)。

問題來了,事情並不會總是這麼簡單,假如函式

g

是開啟一個執行緒去處理

v

,那麼有可能線上程函式中訪問

v

時,

f

已經返回,

v

離開作用域被析構,此時訪問

v

是不合法的。

在這裡,你可以使用換個思路,使用移動語義轉移控制權,延長

vector

的生命週期到執行緒函式結束。

void

threadFunc

std

::

vector

<

int

>&&

v

{

/* 。。。 */

}

void

g

std

::

vector

<

int

>&

v

{

std

::

thread

task

{

threadFunc

std

::

move

v

)};

// C++14 可以直接傳 lambda 來捕獲右值引用

task

detach

();

}

void

f

()

{

std

::

vector

<

int

>

v

{

1

2

3

};

// f 對 v 處理完畢後交給 g

g

v

);

// 傳給 g 之後,f 不能再處理 v 了

}

當然,你不在意開銷的話,可以這麼搞:

using

VectorInt

=

std

::

vector

<

int

>

using

SharedVectorInt

=

std

::

shared_ptr

<

VectorInt

>

void

g

SharedVectorInt

pv

{

std

::

thread

task

([

pv

{

/* 。。。 */

});

task

detach

();

}

void

f

()

{

SharedVectorInt

pv

new

VectorInt

{

1

2

3

});

// f 可以在呼叫 g 之前處理

g

pv

);

// f 也可以在呼叫 g 之後處理

}

開銷有哪些?首先

vector

本身是分配在堆上的,涉及一次小記憶體的申請(內部的三個指標)。其次,

shared_ptr

內部維護了引用計數,會涉及到若干次引用計數的增加和減少,引用計數基本上是

atomic

型別,自增和自減開銷都比一般的整型大。

但實際上呢,很多時候這點開銷相比整個處理流程的開銷,算不上什麼。所以我為何不這麼寫呢?

void

f

List

<

Integer

>

list

{

/* 。。。 */

g

list

);

/* 。。。 */

}

void

g

List

<

Integer

>

list

{

/* 。。。 */

}

再回頭來看,如果

vector

的複製變成淺複製,也就是所謂的引用語義,那麼你要考慮:這個引用,要不要用一個引用計數來維護?

不要:存在生命週期的問題,使用者很容易瞎用導致崩。(雖然本來很多寫慣了其他語言的來寫 C++ 就容易崩)

要,OK,那我就是想要壓榨效能,就是不想要引用計數增減的開銷,我要怎麼做?傳遞

vector&

當作開銷小的引用,傳遞

vector

當作開銷大的引用?你覺得這樣設計好嗎?

為什麼C++的STL被設計成值語義,而非更加常見的引用語義?zihuatanejo2020-05-19 18:01:37

跟題主的理解恰恰相反,通常值語義的效能會更好。這是因為值語義會使資料結構的記憶體佈局更緊湊,更能讓編譯器和cache發揮作用,而這是引用語義無法做到的。例如陳碩在其部落格上有文章進行了講述,知乎上也有不少文章和回答進行了闡述。

至於所謂的“深複製”往往不是必然的,程式設計師有足夠的的決定權來選擇“深”與“淺”。

為什麼C++的STL被設計成值語義,而非更加常見的引用語義?d41d8c2020-05-21 15:25:48

其他語言用引用語義是因為只有引用語義……

為什麼C++的STL被設計成值語義,而非更加常見的引用語義?一鳴道長2020-05-23 02:18:57

你以為引用語義的語言就是記憶體佈局變一下,實際上還多了GC,對效能的影響就不像C++那麼好控制了