C++中const——由一个例子想到的
前天同學實現了《C++ Primer》中關于虛函數的一個例子,拿過來問我,代碼如下:
#include<iostream> #include<string> using namespace std;class Item{ public:Item(const string x,const double y){isbn=x;price=y;};virtual double net_price(size_t n) const{return n*price;};//可以去掉virtual體驗到第3、4行打印出來的區別virtual ~Item(){};string isbn; protected:double price; };class Bulk_Item:public Item{ public:Bulk_Item(const string x,const double y,const size_t z,const double w):Item(x,y),min_qty(z),discount(w){};double net_price(size_t n) const;//double get() const;//double get(); private:size_t min_qty;double discount; };double Bulk_Item::net_price(size_t n) const{if(n>=min_qty) return n*(1-discount)*price;else return n*price; }void print_total(const Item &it,size_t n){cout<<it.isbn<<"\tnumber sold: "<<n<<"\ttotal price: "<<it.net_price(n)<<endl; }int main(){Item it1("201307291540",16.5);cout<<it1.net_price(2)<<endl;Bulk_Item it2("201307291545",22.5,10,0.2);cout<<it2.net_price(20)<<endl;print_total(it1,4);print_total(it2,20);return 0; }?
?同學的疑問是,如果去掉 Item 類定義中
virtual double net_price(size_t n) const{return n*price;};?
一句中的 const 限定,則會在print_total 的定義行出現編譯錯誤
“Item::net_price”: 不能將“this”指針從“const Item”轉換為“Item &”他覺得定義的 net_price 方法并沒有修改 Item 內的屬性,有沒有const限定詞應該是一樣的。
但實際上,在print_total 的定義中,限定了引用 it 為 const, 這意味著該引用只能調用 Item 類內的 const方法,即:const限定的對象引用不能調用此對象中的非const方法。
進而,可以知道,C++中判斷一個方法是不是const,并不是檢測你在該方法中有沒有改變類的屬性,而只是簡單的看你有沒有const限定詞,有就是const方法,沒有就不是const方法。若有const限定詞,而在方法內你又試圖改變類的屬性,則編譯器會報錯。
?
那么,同名函數(包括參數相同),一個有const限定,一個沒有,是兩個函數還是一個函數?為了探究,在?Bulk_Item 的定義中增加兩個 get_price 函數,一個為const,一個為普通函數,然后在主函數里調用:
#include<iostream> #include<string> using namespace std;class Item{ public:Item(const string x,const double y){isbn=x;price=y;};virtual double net_price(size_t n) const{return n*price;};//可以去掉virtual體驗到第3、4行打印出來的區別virtual ~Item(){};string isbn; protected:double price; };class Bulk_Item:public Item{ public:Bulk_Item(const string x,const double y,const size_t z,const double w):Item(x,y),min_qty(z),discount(w){};double net_price(size_t n) const;double get_price() const{cout<<"get const"<<endl;return price;};double get_price(){cout<<"get Non const"<<endl;return price; };private:size_t min_qty;double discount; };double Bulk_Item::net_price(size_t n) const{if(n>=min_qty) return n*(1-discount)*price;else return n*price; }void print_total(const Item &it,size_t n){cout<<it.isbn<<"\tnumber sold: "<<n<<"\ttotal price: "<<it.net_price(n)<<endl; }int main(){Item it1("201307291540",16.5);cout<<it1.net_price(2)<<endl;Bulk_Item it2("201307291545",22.5,10,0.2);cout<<it2.net_price(20)<<endl;print_total(it1,4);print_total(it2,20); cout<<it2.get_price()<<endl;return 0; }?
輸出結果為:
get Non const 22.5可見,并沒有產生編譯錯誤,自動調用的是非const方法。故有無const會造成有兩個重載的函數,編譯后的函數名,除了函數名和參數,還加入了const限定詞。但調用時會優先選非const的函數(這是因為我們傳入的是非const對象it2,下面會看到,若傳入const對象,則自動調用const版本)。
接著,我們構造打印函數,如下:
#include<iostream> #include<string> using namespace std;class Item{ public:Item(const string x,const double y){isbn=x;price=y;};virtual double net_price(size_t n) const{return n*price;};//可以去掉virtual體驗到第3、4行打印出來的區別virtual ~Item(){};string isbn; protected:double price; };class Bulk_Item:public Item{ public:Bulk_Item(const string x,const double y,const size_t z,const double w):Item(x,y),min_qty(z),discount(w){};double net_price(size_t n) const;double get_price() const{cout<<"get const"<<endl;return price;};double get_price(){cout<<"get Non const"<<endl;return price; };private:size_t min_qty;double discount; };double Bulk_Item::net_price(size_t n) const{if(n>=min_qty) return n*(1-discount)*price;else return n*price; }void print_total(const Item &it,size_t n){cout<<it.isbn<<"\tnumber sold: "<<n<<"\ttotal price: "<<it.net_price(n)<<endl; }void print_price(const Bulk_Item &it){cout<<"print_price const"<<endl;cout<<it.get_price()<<endl; }void print_price(Bulk_Item &it){cout<<"print_price Non const"<<endl;cout<<it.get_price()<<endl; }int main(){Item it1("201307291540",16.5);cout<<it1.net_price(2)<<endl;Bulk_Item it2("201307291545",22.5,10,0.2);cout<<it2.net_price(20)<<endl;print_total(it1,4);print_total(it2,20);cout<<it2.get_price()<<endl; print_price(it2);return 0; }?
輸出結果為:
print_price Non const get Non const 22.5?
可見,(1)調用時,優先調用參數為非const的函數(因為傳入的it2是非const參數),編譯器不會在你傳入非const參數時調用const參數的函數(除非沒有非const版本),這是合理的,否則你不能改變你想改變的;(2)函數內,自動調用非const版本get_price(因為傳入的是非const引用,故優先調用get_price方法的非const版本)。
同理,如果傳入const 參數,如下:
#include<iostream> #include<string> using namespace std;class Item{ public:Item(const string x,const double y){isbn=x;price=y;};virtual double net_price(size_t n) const{return n*price;};//可以去掉virtual體驗到第3、4行打印出來的區別virtual ~Item(){};string isbn; protected:double price; };class Bulk_Item:public Item{ public:Bulk_Item(const string x,const double y,const size_t z,const double w):Item(x,y),min_qty(z),discount(w){};double net_price(size_t n) const;double get_price() const{cout<<"get const"<<endl;return price;};double get_price(){cout<<"get Non const"<<endl;return price; };private:size_t min_qty;double discount; };double Bulk_Item::net_price(size_t n) const{if(n>=min_qty) return n*(1-discount)*price;else return n*price; }void print_total(const Item &it,size_t n){cout<<it.isbn<<"\tnumber sold: "<<n<<"\ttotal price: "<<it.net_price(n)<<endl; }void print_price(const Bulk_Item &it){cout<<"print_price const"<<endl;cout<<it.get_price()<<endl; }void print_price(Bulk_Item &it){cout<<"print_price Non const"<<endl;cout<<it.get_price()<<endl; }int main(){Item it1("201307291540",16.5);cout<<it1.net_price(2)<<endl;Bulk_Item it2("201307291545",22.5,10,0.2);cout<<it2.net_price(20)<<endl;print_total(it1,4);print_total(it2,20);cout<<it2.get_price()<<endl;print_price(it2);const Bulk_Item it3("201307291546",44,10,0.2);print_price(it3);return 0; }?
則會打印:
print_price const get const 44?
說明,(1)傳入const參數會調用參數為const的函數,這是理所應當的;(2)在print_price里會調用const版本的get_price,這說明,如果我們對一個類,有同名的兩個函數,一個為const,一個非const,若用一個const對象引用來調用這個同名函數,則自動調用那個const函數。如下所示:
#include<iostream> #include<string> using namespace std;class Item{ public:Item(const string x,const double y){isbn=x;price=y;};virtual double net_price(size_t n) const{return n*price;};//可以去掉virtual體驗到第3、4行打印出來的區別virtual ~Item(){};string isbn; protected:double price; };class Bulk_Item:public Item{ public:Bulk_Item(const string x,const double y,const size_t z,const double w):Item(x,y),min_qty(z),discount(w){};double net_price(size_t n) const;double get_price() const{cout<<"get const"<<endl;return price;};double get_price(){cout<<"get Non const"<<endl;return price; };private:size_t min_qty;double discount; };double Bulk_Item::net_price(size_t n) const{if(n>=min_qty) return n*(1-discount)*price;else return n*price; }void print_total(const Item &it,size_t n){cout<<it.isbn<<"\tnumber sold: "<<n<<"\ttotal price: "<<it.net_price(n)<<endl; }void print_price(const Bulk_Item &it){cout<<"print_price const"<<endl;cout<<it.get_price()<<endl; }void print_price(Bulk_Item &it){cout<<"print_price Non const"<<endl;cout<<it.get_price()<<endl; }int main(){Item it1("201307291540",16.5);cout<<it1.net_price(2)<<endl;Bulk_Item it2("201307291545",22.5,10,0.2);cout<<it2.net_price(20)<<endl;print_total(it1,4);print_total(it2,20);cout<<it2.get_price()<<endl;print_price(it2);const Bulk_Item it3("201307291546",44,10,0.2);cout<<it3.get_price()<<endl;print_price(it3);return 0; }?則會打印:
get const 44?故而,鑒于有無const限定詞會造就兩個不同的函數,所以如果基類中有const,而繼承類中同名方法沒有const,則其實繼承類實現的是一個完全新的函數,而不是在覆蓋基類的方法。向上類型轉換時,會調用基類的方法,而不是繼承類中同名的非const方法,如下:
#include<iostream> #include<string> using namespace std;class Item{ public:Item(const string x,const double y){isbn=x;price=y;};virtual double net_price(size_t n) const{return n*price;};//可以去掉virtual體驗到第3、4行打印出來的區別virtual ~Item(){};string isbn; protected:double price; };class Bulk_Item:public Item{ public:Bulk_Item(const string x,const double y,const size_t z,const double w):Item(x,y),min_qty(z),discount(w){};double net_price(size_t n) ;private:size_t min_qty;double discount; };double Bulk_Item::net_price(size_t n) {if(n>=min_qty) return n*(1-discount)*price;else return n*price; }void print_total(const Item &it,size_t n){cout<<it.isbn<<"\tnumber sold: "<<n<<"\ttotal price: "<<it.net_price(n)<<endl; }int main(){Item it1("201307291540",16.5);cout<<it1.net_price(2)<<endl;Bulk_Item it2("201307291545",22.5,10,0.2);cout<<it2.net_price(20)<<endl;print_total(it1,4);print_total(it2,20);return 0; }最后一行輸出為450而不是360。
同理,若基類方法無const,而繼承類中有,則向上類型轉換還是會調用基類的方法。
總結一下,主要有這么幾點:
1、const限定的對象引用不能調用此對象中的非const方法。
2、const關鍵詞限定的方法,則編譯器就認為它是const型的,即使它并沒有改變對象的任何屬性。若限定了const,又在方法內改變了屬性,則編譯器會報錯。
3、同名函數(參數也相同),一個有const限定,一個沒有,編譯器認為是兩個重載函數,不會報錯。這就意味著C++中編譯器生成函數名時,除了函數名、參數外還有const,由三者共同組成編譯后的函數名。
4、如上,兩個方法的方法名和參數完全相同,一個有const限定,一個沒有,則若你用const對象引用調用,會自動調用const版本的方法;若用非const對象引用調用,則會自動調用非const的版本。
5、同樣的函數,只是參數一個是const,一個非const,則你用const參數調用,會自動調用參數為const的版本;你用普通參數調用,會自動調用參數為普通參數的版本,若此時沒有非const的版本,它才會轉而調用const版本的。
6、如果基類中有const,而繼承類中同名方法沒有const,則其實繼承類實現的是一個完全新的函數,而不是在覆蓋基類的方法,向上類型轉換時,會調用基類的方法,而不是繼承類中同名的非const方法。
突然想到,學習一門語言的最好方式也就就是:寫一個這個語言的編譯器。這樣你就能對它的限制,它能做什么和不能做什么,你能用它做什么和不能做什么,有比較深刻的理解和記憶。推而廣之,人類語言有很多冗余性,語法限制也相對比較寬松,故人類語言的編譯器應該很難寫。若能寫一個人類語言的編譯器,那就是真正的自然語言理解。
轉載于:https://www.cnblogs.com/rolling-stone/p/3223454.html
總結
以上是生活随笔為你收集整理的C++中const——由一个例子想到的的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Event Aggregator
- 下一篇: 一般学校如何让学生实现梦想的?