在C++語言中,每個名字都有作用域,每個對象都有生命期;名字的作用域指的是該名字的程序文本區,對象的生命期是在程序的執行過程中對象的存在時間。
自動對象
1、只有當定義它的函數被調用時才存在的對象稱為自動對象。自動對象在每次調用函數時創建和撤銷
2、對於未初始化的內置類型的局部變量,其初值不確定。
3、形參也是自動對象,形參所占用的存儲空間在調用函數時創建,而在函數結束時撤銷。
靜態局部對象
一個變量如果位於函數的作用域內,但生命期跨越了這個函數的多次調用,這種變量往往很有用。則應該將這樣的對象定義為static(靜態的)。
static局部對象確保不遲於在程序執行流程第一次經過該對象的定義語句時進行初始化(比較難於理解o(∩∩)o...)。這種對象一旦被創建,在程序結束前都不會撤銷。當定義靜態局部對象的函數結束時,靜態局部對象不會撤銷。在該函數被多次調用的過程中,靜態局部對象會持續存在並保持它的值。
/* *在第一次調用函數count_calls之前,cnt就已經創建並賦初值為0 *在執行函數 count_calls 時, 變量 ctr 就已經存在並且保留上次調用該函數時的值。 */ size_t count_calls() { static size_t cnt = 0; return ++cnt; } int main() { for (int i = 0; i != 10; ++i) { cout << count_calls() << endl; } return 0; }
//P221 習題7.27 用static實現階乘 int factorial() { static int j = 0; static int res = 1; ++j; return (res *= j); } int main() { for (int i = 0; i != 5; ++i) { cout << factorial() << endl; } }
使用“小操作”函數的好處:
1)閱讀和理解函數的調用,要比讀一條用等價的表達式要容易得多。
2)如果需要做任何修改,修改函數要比找出並修改每一處等價表達式容易得多。
3)使用函數可以確保統一的行為,每個測試都保證以相同的方式實現。
4)函數可以重用,不必為其他應用重寫代碼。
但是這樣的函數也存在者一些缺陷,調用函數比求解等價表達式要慢得多。在大多數的機器上,調用函數都要做很多工作:
1)調用前先保存寄存器,並且要在返回時恢復
2)復制實參
3)程序必須轉向一個新的位置執行
1、使用內聯函數避免函數調用的開銷
將函數指定為inline函數,就是將它在程序中每個調用點上“內聯地”展開。從而消除了把表達式寫成函數的額外執行開銷。
inline const string &shorterString(const string &s1,const string &s2) { return (s1.size() < s2.size() ? s1 : s2); }
內聯說明(inline)對於編譯器來說只是一個建議,編譯器可以選擇忽略這個建議;一般來說,內聯機制使用與優化小的,只有幾行而且經常被調用的函數。
2、把內聯函數寫入頭文件
內聯函數要在頭文件中定義,這一點不同於其他函數!
在頭文件中加入或修改內聯函數時,使用了該頭文件的所有源文件都必須重新編譯!因為修改此處的頭文件相當於修改了各個源文件!
//P222 習題7.39 inline bool isShorter(const string &str1,const string &str2) { return str1.size() < str2.size(); }
class Sales_item { public: double avg_price() const; bool same_isbn(const Sales_item &rhs) const { return isbn == rhs.isbn; } private: std::string isbn; unsigned int units_sold; double revenue; };
函數原型必須在類中定義,而函數體既可以在類中定義,也可以在類外定義,一般比較短小的函數定義在類的內部。
編譯器隱式的將在類內定義的成員函數當作內聯函數。
類的成員函數可以訪問該類的private成員。
1、成員函數含有額外的、隱含的形參
每個成員函數都有一個額外的、隱含的形參將該成員函數與調用該函數的類對象綁定在一起。而這個形參就是this指針!
因此,語句
total.sime_isbn(trans);
就如同編譯器這樣重寫了這個函數調用:
Sales_item::same_isbn(&total,trans);
2、const成員函數的引入
const改變了隱含的this形參的類型。在調用total.same_isbn(trans)時,隱含的this形參將是一個指向total對象的constSales_Item*類型的指針:
bool Sales_item::same_isbn(const Sales_item *const this, const Sales_item &rhs) const { return (this -> isbn == rhs.isbn); }
使用這種方式的const函數稱為常量成員函數。由於this是指向const對象的指針,const成員函數不能修改調用該函數的對象,因此,函數avg_price和函數same_isbn只能讀取而不能修改調用他們的對象的數據成員。
3、this指針的使用
由於this指針是隱式定義的,因此不需要在函數的形參表中包含this指針,實際上,這樣做也是非法的。但是,在函數體中可以顯式地使用this指針。如:
bool Sales_item::same_isbn(const Sales_item &rhs) const { return (this -> isbn == rhs.isbn); } double Sales_item::avg_price() const { if (units_sold) return (this -> revenue) / (this -> units_sold); else return 0; }
在類外定義成員函數時,返回類型和參數表必須和函數聲明一致,如果函數被聲明為const成員函數,那麼函數定義時形參表後面也必須有const!
4、定義構造函數
Sales_item::Sales_item():units_sold(0),revenue(0){};
1)通常構造函數會作為類的接口的一部分,因此必須將構造函數定義為public的。
2)在冒號和花括號之間的代碼稱為構造函數的初始化列表。構造函數的初始化列表為類的一個或多個數據成員指定初值。它跟在構造函數的形參表之後,以冒號開頭。構造函數的初始化式是一系列成員名,每個成員後面是括在圓括號中的初始值。多個成員的初始化用逗號分隔。
3)如果沒有為一個類顯式定義任何構造函數,編譯器將自動為這個類生成默認構造函數。由編譯器創建的默認構造函數通常稱為合成的默認構造函數。
對於具有類類型的成員,如isbn,則會調用該成員所屬類自身的默認構造函數實現初始化。而內置類型成員的初值卻要依賴於對象如何定義,如果對象定義為全局對象,或定義為靜態局部對象,則將這些成員初始化為0,不然,則不提供這些成員的初始化工作,這些成員沒有初始化!
【建議:】
合成的默認構造函數一般適用於僅包含類類型成員的類。而對於含有內置類型或復合類型成員的類,則通常應該定義他們自己的默認構造函數初始化這些成員。
5、類代碼文件的組織【摘抄】
通常情況下,將類的聲明放置在頭文件中,在類外定義的成員函數放置在源文件中,C++程序員習慣使用一些簡單的規則給頭文件及其關聯的類定義代碼命名。類定義應置於名為type.h或type.H的文件 中,type指在該文件中定義的類的名字。成員函數的定義則一般存儲在與類同名的源文件中。依照這些規則,我們將類Sales_item放在名為Sales_item.h的文件中定義。任何需使用這個類的程序,都必須包含這個頭文件。而Sales_item的成員函數的定義則應該放在名為Sales_item.cc的文件中。這個文件同樣也必須包含Sales_item.h頭文件。
//P227 習題7.31 class Sales_item { public: Sales_item():units_sold(0),revenue(0) {}; double avg_price() const; bool same_isbn(const Sales_item &rhs) const; void scan(); void print() const; private: std::string isbn; unsigned int units_sold; double revenue; }; bool Sales_item::same_isbn(const Sales_item &rhs) const { return (this -> isbn == rhs.isbn); } double Sales_item::avg_price() const { if (units_sold) return (this -> revenue) / (this -> units_sold); else return 0; } void Sales_item::scan() { cout << "Please input the isbn、units_sold & price" << endl; double price; cin >> isbn >> units_sold >> price; revenue += units_sold * price; } void Sales_item::print() const { cout << "ISBN: " << isbn << endl; cout << "Units_sold: " << units_sold << endl; cout << "Revenue: " << revenue << endl; cout << "Avg_price: " << avg_price() << endl; } int main() { // freopen("input.txt","r",stdin); Sales_item item; item.scan(); item.print(); }