【足迹C++primer】52、,转换和继承虚函数
轉換和繼承,虛函數
Understanding conversions between base and derived classes is essential to
understanding how object-oriented programming works in C++.
理解基類和派生類之間的轉換是不可缺少的 理解面向對象編程在。
Like built-in pointers, the smart pointer classes (§12.1, p. 450) support the
derived-to-base conversion—we can store a pointer to a derived object in a
smart pointer to the base type.
像內置指針,智能指針類支持 導出到基類的轉換能夠存儲一個指向派生類對象的一個
基類類型的指針。
Static Type and Dynamic Type(靜態和動態類型)
class Quote
{
public:Quote()=default;Quote(const string &book, double sales_price):bookNo(book), price(sales_price){cout<<"Quote gouzhao function"<<endl;}string isbn() const {return bookNo;}//返回指定數量的項目總銷售價格//派生類將重寫應用不同的折扣算法virtual double net_price(size_t n) const {return n*price;}virtual ~Quote()=default; //動態鏈接析構函數
private:string bookNo; //這本書的isbn號
protected:double price=0.0; //不打折的價格};//繼承,怎樣繼承?
class Bulk_quote : public Quote
{
public:Bulk_quote()=default;Bulk_quote(const string & book, double p, size_t qty, double disc): Quote(book, p), min_qty(qty), discount(disc){cout<<"Bulk_quote construct function"<<endl;}//重寫虛函數double net_price(size_t n) const override {cout<<"double net_price(size_t)"<<endl; return n*price;}//再次說明,請聲明函數后一定要記得定義它。不然我這是出了各種莫名其妙的錯誤!
// ~Bulk_quote2(){cout<<"~Bulk_quote2()"<<endl;}
private:size_t min_qty=0;double discount=0.0;
};
There Is No Implicit Conversion from Base to Derived ...
void fun1()
{Quote base;
// Bulk_quote* bulkP=&base; 錯誤不能把基類轉換成派生類
// Bulk_quote& bulkRef=base; 同上Bulk_quote bulk;Quote *itemP=&bulk; //派生類轉換成基類
// Bulk_quote *bulkP=itemP; error:基類到派生類
}
...and No Conversion between Objects
void fun2()
{Bulk_quote bulk; //派生類Quote item(bulk); //調用基類Quote的賦值構造函數item=bulk; // calls Quote::operator=(const Quote&)拷貝賦值運算符
}
這里當我們的參數對象是基類的時候。轉換的時候僅僅有基類部分會被拷貝。而派生的那部分
會直接被忽略
Virtual Functions虛函數
Key Concept: Conversions among Types Related by InheritanceThere are three things that are important to understand about conversions
among classes related by inheritance:
? The conversion from derived to base applies only to pointer or reference
types.
? There is no implicit conversion from the base-class type to the derived
type.
? Like any member, the derived-to-base conversion may be inaccessible due
to access controls.
關鍵概念:繼承關系的類型之間的轉換
因繼承而相關聯的類中轉換的重要的三件事 :
?從派生類到基類的轉換僅僅適用于指針或引用 類型。
?有從基類到派生類沒有隱式轉換
?像不論什么成員,派生類到基類的轉換可能無法訪問因為 訪問控制。
動態綁定
double print_total(ostream &os, const Quote &item, size_t n)
{//依據不同的對象來綁定到這個參數的類型//這里引用Quote::net_price 或 Bulk_quote::net_pricedouble ret=item.net_price(n);os<<"ISBN: "<<item.isbn() //調用 Quote::isbn<<" # sold: "<<n<<" total due: "<<ret<<endl;return ret; //這里上面的Quote參數是能夠接受Quote或者Bulk_quote類型的
}
Calls to Virtual Functions May Be Resolved at Run Time
調用的虛函數能夠在執行時確定
void fun3()
{Quote base("0-201-82470-1", 50);print_total(cout, base, 10); //調用Quote的net_priceBulk_quote derived("0-201-82470-1", 50, 5, 0.19);print_total(cout, derived, 10); //調用Bulk_quote的net_pricebase=derived; //吧quote類型的部分復制到basebase.net_price(20);
}
Virtual Functions in a Derived Class(【派生類中的虛函數)
A function that is virtual in a base class is implicitly virtual in itsderived classes. When a derived class overrides a virtual, the parameters in
the base and derived classes must match exactly.
The final and override Specifiers
(1) ? ? ? 重載的幾個函數必須在同一個類中。覆蓋的函數必須在有繼承關系的不同的類中
(2) ? ? ? 覆蓋的幾個函數必須函數名、參數、返回值都同樣;
重載的函數必須函數名同樣,參數不同。參數不同的目的就是為了在函數調用的時候編譯器可以通過參數來推斷程序是在調用的哪個函數。
這也就非常自然地解釋了為什么函數不能通過返回值不同來重載。由于程序在調用函數時非常有可能不關心返回值,編譯器就無法從代碼中看出程序在調用的是哪個函數了。
(3) ? ? ? 覆蓋的函數前必須加keywordVirtual;
重載和Virtual沒有不論什么瓜葛,加不加都不影響重載的運作。
struct B {virtual void f1(int) const;virtual void f2();void f3(); };struct D1 : B //這是什么繼承? {void f1(int) const override; //ok能夠覆蓋 // void f2(int) override; error沒有f2(int)這個虛函數 // void f3() override; //error:f3()不是虛函數 // void f4() override; error:沒有f4()這個虛函數 };
final這個keyword
struct D2 : B
{//從B繼承f2,f3之后我們override f1void f1(int) const final; //之后派生類無法覆蓋f1
};struct D3 : D2
{void f2();
// void f1(int) const; //注意:這個函數是被final修飾的函數
};
Virtual Functions and Default Arguments(虛函數和默認參數)
具有默認參數的虛函數應該使用同樣的參數 在基值和派生類。Circumventing the Virtual Mechanism
void fun4()
{cout<<"there is fun4"<<endl;Quote *baseP;double undiscounted = baseP->Quote::net_price(42);
}
Ordinarily, only code inside member functions (or friends) should need to use
the scope operator to circumvent the virtual mechanism
通常僅僅有類里面的成員函數(友元函數)須要使用作用域操作符來規避虛擬機制
15.4. Abstract Base Classes
抽象類就是類里定義了純虛成員函數的類為什么要定義抽象基類呢?依我所見主要有下面原因:
1.最重要的原因是,能夠將接口與實現分離。
接口是軟件產品最有價值的資源。
設計接口比實現接口須要耗費更昂貴的成本。
因此,要將接口保護起來,
以免在針對客戶需求改動實現的時候,程序猿不小心把接口破壞掉。
2.引入抽象基類和純虛函數方便實現C++的多態特性。
能夠用抽象基類的指針去調用子類對象的方法。
3.非常多時候。很多基類被實例化是不合理的。
比如“形狀”這個基類,被實例化之后反而會讓人相當費解,
所以干脆將“形狀”這個類定義為抽象類,由它派生出正方形,三角形等子類。
純虛函數
當類聲明中包括純虛函數時。則不能創建該類的對象。
基類的純虛函數必須有“=0”,但不一定沒有函數的實現,僅僅是不能直接內嵌在類中。
class Disc_quote : public Quote { public:Disc_quote()=default;Disc_quote(const string & book, double price, size_t qty, double disc):Quote(book, price), quantity(qty), discount(disc) {cout<<"Disc_quote構造函數"<<endl;}double net_price(size_t) const = 0; //純虛函數 protected:size_t quantity=0;double discount=0.0; };
純虛函數不能直接在類里面進行定義。要定義就要在外面
virtual void Move(int nx, int ny) = 0;
void BaseEllipse::Move(int nx, int ny) {x = nx; y = ny;}
這樣是同意的
含有純虛函數的類就是抽象類
void fun5()
{cout<<"there is fun5"<<endl;
// Disc_quote discount; //error:Disc_quote是一個抽象類,含有純虛函數沒法實例化Bulk_quote bulk; //ok,這個里面沒有純虛函數,不是抽象類
}
A Derived Class Constructor Initializes Its Direct Base Class Only
就是派生類參數列表能夠直接初始化基類
class Bulk_quote2 : public Disc_quote
{
public:Bulk_quote2()=default;//直接初始化Bulk_quote2(const string& book, double price, size_t qty, double disc):Disc_quote(book, price, qty, disc) {}double net_price(size_t) const override; //覆蓋純虛函數
};
有些人認為,做人要真,所以說話要直。結果就到處直來直去得罪人。事實上大錯。中國人寫"真"字,是"直"以下兩點。也就是說,一些實話、直話,也要保留兩點。不要所有說出去。實話實說是真,但實話全說就是蠢。肚子里藏不住話的人,自以為說真話。事實上只是是情商不夠而已。
不幸的是,作者好像既不真也不直~~~~~
版權聲明:本文博客原創文章,博客,未經同意,不得轉載。
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的【足迹C++primer】52、,转换和继承虚函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 将clover安装到硬盘EFI分区, 解
- 下一篇: 转:char*, char[] ,CSt