條款45 弄清C 在幕後為你所寫 所呼叫的函式

2022-03-17 19:21:39 字數 2393 閱讀 1976

如果你沒有宣告下列函式,體貼的編譯器會宣告它自己的版本。這些函式是:乙個拷貝建構函式,乙個賦值運算子,乙個析構函式,一對取址運算子。另外,如果你沒有宣告任何建構函式,它也將為你宣告乙個預設建構函式。所有這些函式都是公有的。換句話說,如果你這麼寫:

class empty{};

和你這麼寫是一樣的:

class

empty ;

假設編譯器為你寫了函式,這些函式又做些什麼呢?是這樣的,預設建構函式和析構函式實際上什麼也不做,它們只是讓你能夠建立和銷毀類的物件。注意,生成的析構函式一般是非虛擬的(參見條款14),除非它所在的類是從乙個宣告了虛析構函式的基類繼承而來。預設取址運算子只是返回物件的位址。這些函式實際上就如同下面所定義的那樣:

inline empty::empty() {}

inline empty::~empty() {}

inline empty * empty::operator&()

inline

const empty * empty::operator&() const

至於拷貝建構函式和賦值運算子,官方的規則是:預設拷貝建構函式(賦值運算子)對類的非靜態資料成員進行 "以成員為單位的" 逐一拷貝構造(賦值)。即,如果m是類c中型別為t的非靜態資料成員,並且c沒有宣告拷貝建構函式(賦值運算子),m將會通過型別t的拷貝建構函式(賦值運算子)被拷貝構造(賦值)---- 如果t有拷貝建構函式(賦值運算子)的話。如果沒有,規則遞迴應用到m的資料成員,直至找到乙個拷貝建構函式(賦值運算子)或固定型別(例如,int,double,指標,等)為止。預設情況下,固定型別的物件拷貝構造(賦值)時是從源物件到目標物件的 "逐位" 拷貝。對於從別的類繼承而來的類來說,這條規則適用於繼承層次結構中的每一層,所以,使用者自定義的建構函式和賦值運算子無論在哪一層被宣告,都會被呼叫。

編譯器生成的賦值運算子要想正常工作,與此相關的所有**必須合法且行為上要合理。如果這兩個條件中有乙個不成立,編譯器將拒絕為你的類生成operator=

class

namedobject

private

:

string &namevalue;

const

intobjectvalue;

};void

main()

賦值之前,p.namevalue指向某個string物件,s.namevalue也指向乙個string,但並非同乙個。賦值會給p.namevalue帶來怎樣的影響呢?賦值之後,p.namevalue應該指向 "被s.namevalue所指向的string" 嗎,即,引用本身應該被修改嗎?如果是這樣,那太陽從西邊出來了,因為c++沒有辦法讓乙個引用指向另乙個不同的物件(參見條款m1)(這點其實不用考慮,因為肯定是賦值而不是改變引用指向的物件,但是因為以下的原因,所以不能賦值,若賦值,其他指向那個string的指標或引用也會受到影響)。或者,p.namevalue所指的string物件應該被修改嗎? 這樣的話,含有 "指向那個string的指標或引用" 的其它物件也會受影響,也就是說,和賦值沒有直接關係的其它物件也會受影響。這是編譯器生成的賦值運算子應該做的嗎?

面對這樣的難題,c++拒絕編譯這段**。如果想讓乙個包含引用成員的類支援賦值,你就得自己定義賦值運算子。對於包含const成員的類(例如上面被修改的類中的objectvalue)來說,編譯器的處理也相似;因為修改const成員是不合法的,所以編譯器在隱式生成賦值函式時也會不知道怎麼辦。還有,如果派生類的基類將標準賦值運算子宣告為private,  編譯器也將拒絕為這個派生類生成賦值運算子。因為,編譯器為派生類生成的賦值運算子也應該處理基類部分(見條款16和m33),但這樣做的話,派生類物件就得呼叫對派生類來說無權訪問的基類成員函式,這當然是不可能的。

指標與引用的區別

1.在任何情況下都不能使用指向空值的引用,乙個引用必須總是指向某些物件,所以引用定義時要被初始化。因此如果你使用乙個變數並讓它指向乙個物件,但是該變數在某些時候也可能不指向任何物件,這時你應該把變數宣告為指標,因為這樣你可以賦空值給該變數

2.指標可以被重新賦值以指向另乙個不同的物件,但是引用總是指向在初始化時被指定的物件,以後不能改變

string s1("

nancy");

string s2("

clancy");

string &rs =s1;

rs = s2;//

改變了s1及rs的值,但是rs仍然指向s1

3.當過載某個操作符時,應該使用引用。

vectorv(10);

v[5]=10;

如果操作符返回乙個指標,那麼後乙個語句得這樣寫:

*v[5]=10;

總結:當你知道你必須指向乙個物件並且不想改變其指向時,或者在過載操作符為防止不必要的語義誤解時,你不應該使用指標。而在除此之外的其他情況下,則應使用指標。

《Effective C 》 條款44 條款45

templates可以節省時間和避免 重複。對於類似的classes或functions,可以寫乙個class template或function template,讓編譯器來做剩餘的事。這樣做,有時候會導致 膨脹 code bloat 其二進位製碼帶著重複 或幾乎重複 的 資料,或者兩者。但這時候...

條款45 運用成員函式模板接受所有相容型別

條款44 運用成員函式模板接受所有相容型別 use member function templates to accept all compatible types.內容 不知道大家注意到沒有,在類的繼承體系中,物件指標一直在為我們做一件很好的事情 支援隱式轉換 implicit conversio...

弄清C 在幕後為你做的

乙個空類什麼時候不是空類?當c 編譯器通過它的時候。如果你沒有宣告下列函式,體貼的編譯器會宣告它自己的版本。這些函式是 乙個拷貝建構函式,乙個賦值運算子,乙個析構函式,一對取址運算子。另外,如果你沒有宣告任何建構函式,它也將為你宣告乙個預設建構函式。所有這些函式都是公有的。換句話說,如果你這麼寫 c...