Visual C++ 2008入门经典 第九章类的继承和虚函数
生活随笔
收集整理的這篇文章主要介紹了
Visual C++ 2008入门经典 第九章类的继承和虚函数
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
// 第九章類的繼承和虛函數
//.cpp: 主項目文件。
//1 繼承如何與面向對像的編程思想適應
//2 根據現有類定義新類
//3 使用protected關鍵字為類成員指定新的訪問特性
//4 如何使某個類成為另一個類的友元
//5 虛函數及其使用方法
//6 純虛函數
//7 抽像類
//8 虛析構函數的概念,何時時使用虛析構函數//9.2 類的繼承
//派生類不繼承的基類成員僅有析構函數,構造函數以及任何重載賦值運算符的成員函數
//9.2.1 基類的概念#include "stdafx.h"
#include <iostream>
using namespace std;
using namespace System;
//9.2.2 基類的派生類
/*class CBox
{
public:double m_Length;double m_Width;double m_Height;CBox(double lv = 1.0,double wv =1.0, double hv = 1.0):m_Length(lv), m_Width(wv), m_Height(hv){ }
};//定義CCandBox
class CCandBox: public CBox
{
public:char* m_Contents;int count;CCandBox(char* str = "Candy"){m_Contents = new char[strlen(str) +1];strcpy_s(m_Contents, strlen(str)+1, str);//strcpy_s三個參數,復制操作目標,目標緩沖區的長度和數據原//如果兩個數組都是靜態的,即都不在堆上分配的,那么我們可以省略第二個實參,而只提供目標和數據擾的指針//這樣做是可以的,因為strcpy_s()函數還可以用作能夠自動推斷目標字符串長度的模板函數,因此//當我們處理靜態字符串時,調用該函數只提供目標少源字符串作為參數即可}~CCandBox(){delete []m_Contents;}
};
int main(array<System::String ^> ^args)
{CBox myBox(4.0, 3.0, 2.0);CCandBox myCandyBox;CCandBox myMintBox("Wafet Thin Mints");cout<<endl;cout<<"myBox 的大小為"<<sizeof(myBox)<<"bytes"<<endl;cout<<"myCandyBox 的大小為"<<sizeof(myCandyBox)<<"bytes"<<endl;cout<<"myMintBox 的大小為"<<sizeof(myMintBox)<<"bytes"<<endl;//兩個CCandBox對像大小相同,都占用32個字切,字符串的長度不影響對像的大小,因為容納字符串內存是在自由存儲器上分配的//32個字節的構成情況如下:從基類CBox繼承了3個double成員總共占用了24個字節//指針成員m_Contents要占用4個字節,加一起來是28。//編譯器要在8字節的倍數地址上對齊成員,通過給CCandyBox類添加一個成員---比如int類型,應該能夠證實這一點//我們將發現,類對像的大小仍然是32個字節system("pause");return 0;
}*///9.3 繼承機制下的訪問控制
/*class CBox
{
public:CBox(double lv = 1.0,double wv =1.0, double hv = 1.0):m_Length(lv), m_Width(wv), m_Height(hv){ }double Volume(){return m_Length * m_Width * m_Height;}
private:double m_Length;double m_Width;double m_Height;
};//定義CCandBox
class CCandBox: public CBox
{
public:char* m_Contents;int count;CCandBox(char* str = "Candy"){m_Contents = new char[strlen(str) +1];strcpy_s(m_Contents, strlen(str)+1, str);}//9.3.1 派生類中的構造函數的操作CCandBox(double lv, double wv, double hv, char* str="Candy"):CBox(lv, wv, hv){cout<<"執行派生類的構造函數"<<endl;m_Contents = new char[strlen(str)+1];strcpy_s(m_Contents, strlen(str)+1, str);}//除了在派生類中添加了一個構造函數以外,我們還每個構造函數中添加了一條輸出語句//對CBox類構造函數的顯式調用出現在派生類構造函數函數頭包含的冒號之后,//注意:基類構造函數總是先于派生類構造函數被調用//而基類的析構函數總是后于派生類的析構函數被調用//double Volume()const//{//return m_Length * m_Width * m_Height;//這里直接訪問CBox的私有成員是非法的//}~CCandBox(){delete []m_Contents;}
};
int main(array<System::String ^> ^args)
{CBox myBox(4.0, 3.0, 2.0);CCandBox myCandyBox;CCandBox myMintBox(1.0, 2.0, 3.0, "Wafet Thin Mints");cout<<endl;cout<<"myBox 的大小為"<<sizeof(myBox)<<"bytes"<<endl;cout<<"myCandyBox 的大小為"<<sizeof(myCandyBox)<<"bytes"<<endl;cout<<"myMintBox 的大小為"<<sizeof(myMintBox)<<"bytes"<<endl;cout<<"myCandyBox.Volume()=="<<myCandyBox.Volume()<<endl;cout<<"myMintBox.Volume()=="<<myMintBox.Volume()<<endl;system("pause");return 0;
}*///9.3.2 聲明類的保護成員
//類的保護成員只能被類的成員函數和類的友元函數訪問(還能被友元類的成員函數訪問)
/*class CBox
{
public:CBox(double lv = 1.0,double wv =1.0, double hv = 1.0):m_Length(lv), m_Width(wv), m_Height(hv){ }double Volume(){return m_Length * m_Width * m_Height;}
protected: //保護聲明//它們實際上還是私有成員,但可以被派生類的成員函數訪問double m_Length;double m_Width;double m_Height;
};//定義CCandBox
class CCandBox: public CBox
{
public:char* m_Contents;int count;CCandBox(char* str = "Candy"){m_Contents = new char[strlen(str) +1];strcpy_s(m_Contents, strlen(str)+1, str);}//9.3.1 派生類中的構造函數的操作CCandBox(double lv, double wv, double hv, char* str="Candy"):CBox(lv, wv, hv){cout<<"執行派生類的構造函數"<<endl;m_Contents = new char[strlen(str)+1];strcpy_s(m_Contents, strlen(str)+1, str);}//可以直接計算體積double Volume(){return m_Length * m_Width * m_Height;}~CCandBox(){delete []m_Contents;}
};
int main(array<System::String ^> ^args)
{CBox myBox(4.0, 3.0, 2.0);CCandBox myCandyBox;CCandBox myMintBox(1.0, 2.0, 3.0, "Wafet Thin Mints");cout<<endl;cout<<"myBox 的大小為"<<sizeof(myBox)<<"bytes"<<endl;cout<<"myCandyBox 的大小為"<<sizeof(myCandyBox)<<"bytes"<<endl;cout<<"myMintBox 的大小為"<<sizeof(myMintBox)<<"bytes"<<endl;cout<<"myCandyBox.Volume()=="<<myCandyBox.Volume()<<endl;cout<<"myMintBox.Volume()=="<<myMintBox.Volume()<<endl;system("pause");return 0;
}*/
//派生類對像析構函數調用順序與構造函數相反,這是一條普通適用的規則
//創建對像時首先調用基類的構造函數,然后調用派生類的構造函數
//而銷毀對像時首先調用派生類的析構函數,然后調用基類的析構函數//9.3.3 繼承類成員的訪問級別
//1 公有繼承一切不變
//2 保護繼承,公有成員變保護成員,保護成員變私有成員
//3 私有繼承,公有成員、保護成員變私有成員//書上寫的
//如果基類被聲明為private, 其成員派生類中永遠都不可訪問
//如果基類被聲明為public, 其成員在派生類中的訪問級別保持不變
//如果基類被聲明為protected,基public成員在派生類中將成為protected, protected不變//9.4 派生類中的復制構造函數
//記信,復制構造函數是在聲明用同類對像初始化的對像時被自動調用的
//CBox myBox(2.0, 3.0, 4.0);
//CBox copyBOx(myBox);
/*class CBox
{
public:CBox(double lv = 1.0,double wv =1.0, double hv = 1.0):m_Length(lv), m_Width(wv), m_Height(hv){ }double Volume(){return m_Length * m_Width * m_Height;}//我們還要記得,為了避免無窮無盡的調用自身,復制構造函數的形參必須被指定為引用//否則需要復制以傳值方式傳遞的實參,CBox(const CBox& initBox){cout<<"類的復制構造函數"<<endl;m_Length = initBox.m_Length;m_Width = initBox.m_Width;m_Height = initBox.m_Height;}protected: //保護聲明//它們實際上還是私有成員,但可以被派生類的成員函數訪問double m_Length;double m_Width;double m_Height;
};//定義CCandBox
class CCandBox: public CBox
{
public:char* m_Contents;int count;CCandBox(char* str = "Candy"){m_Contents = new char[strlen(str) +1];strcpy_s(m_Contents, strlen(str)+1, str);}//9.3.1 派生類中的構造函數的操作CCandBox(double lv, double wv, double hv, char* str="Candy"):CBox(lv, wv, hv){cout<<"執行派生類的構造函數"<<endl;m_Contents = new char[strlen(str)+1];strcpy_s(m_Contents, strlen(str)+1, str);}//可以直接計算體積double Volume(){return m_Length * m_Width * m_Height;}//調用一下CBox的復制構造函數CCandBox(const CCandBox& initCB):CBox(initCB){cout<<"執行CCandBox類的構造函數"<<endl;m_Contents = new char[strlen(initCB.m_Contents)+1];strcpy_s(m_Contents, strlen(initCB.m_Contents)+1, initCB.m_Contents);}//在我們編寫派生類的構造函數時,必須確保派生類對像的成員被正確初始化,基中當然包括繼承的成員//也就是基類的成員//在為派生類編寫構造函數時,需要初始化包括繼承成員在內的派生類對像的所有成員~CCandBox(){delete []m_Contents;}
};
int main(array<System::String ^> ^args)
{CBox myBox(4.0, 3.0, 2.0);CCandBox myCandyBox;CCandBox myMintBox(1.0, 2.0, 3.0, "Wafet Thin Mints");cout<<endl;cout<<"myBox 的大小為"<<sizeof(myBox)<<"bytes"<<endl;cout<<"myCandyBox 的大小為"<<sizeof(myCandyBox)<<"bytes"<<endl;cout<<"myMintBox 的大小為"<<sizeof(myMintBox)<<"bytes"<<endl;cout<<"myCandyBox.Volume()=="<<myCandyBox.Volume()<<endl;cout<<"myMintBox.Volume()=="<<myMintBox.Volume()<<endl;CBox copyBox(myBox);cout<<"copyBox.Volume()=="<<copyBox.Volume()<<endl;//剛剛不是說不能繼承復制構造函數嗎? 這個可能有問題CCandBox mycopyBox(myMintBox);cout<<"mycopyBox.Volume()=="<<mycopyBox.Volume()<<endl;system("pause");return 0;
}
*///9.5 友元類成員
/*
#include "stdafx.h"
#include <iostream>
using namespace std;
using namespace System;
//start 類的友元成員/
class F1;
class F2
{
private:int var2;
public:int copy(F1* f1); //在F2中聲明一個函數,叁數為接收F1的地址F2(F1* f1) //構造函數的參數為F1的地址{var2 = copy(f1); //這里F2的構造函數不能直接訪問f1的私有成員,只有通過copy這個友元函數來完成}int GetVar(){return var2;}
};
class F1
{
private:int var;friend int F2::copy(F1* f1);
public:F1(){var = 250;}
};
//最后來定義一個F2::copy()函數
int F2::copy(F1* f1)
{return f1->var;
}
//end 類的友元成員///下面用來定義一打瓶子的包裝箱
class CBottle;
class CCarton
{
public:CCarton(const CBottle& aBottle);double Volume(){cout<<"m_Height:"<<m_Height<<", m_Length:"<<m_Length<<", m_Width:"<<m_Width<<endl;return m_Height*m_Length*m_Width;}private:double m_Height;double m_Width;double m_Length;
};class CBottle
{
public:CBottle(double height, double diameter){m_Height = height;m_Diameter = diameter;}friend CCarton::CCarton(const CBottle& aBottle);
private:double m_Height;double m_Diameter;//這是定義友元函數的模式
};
CCarton::CCarton(const CBottle& aBottle)
{m_Height = aBottle.m_Height;m_Length = 4.0 * aBottle.m_Diameter;m_Width = 3.0 * aBottle.m_Diameter;
}int main(array<System::String ^> ^args)
{//這里只是實現類的友元成員,并不是類的友元類F1 f1;F2 f2(&f1);cout<<"var2:"<<f2.GetVar()<<endl;CBottle cbottle(30, 5);CCarton carton(cbottle);cout<<"carton:"<<carton.Volume()<<endl;system("pause");return 0;
}*///9.6 虛函數
/*#include "stdafx.h"
#include <iostream>
using namespace std;
using namespace System;
class CBox
{
public:void ShowVolume(){cout<<"CBox usable volume is "<<Volume()<<endl;}double Volume(){return m_Height * m_Length * m_Width;}CBox(double mh=1.0, double ml=1.0, double mw=1.0):m_Height(mh), m_Length(ml), m_Width(mw){}protected://任何派生類的成員函數都可以訪問它們double m_Height;double m_Length;double m_Width;
};class CGlassBox: public CBox
{
public:double Volume() const{return 0.85 * m_Length * m_Width * m_Height;}CGlassBox(double mh, double ml, double mw):CBox(mh, ml, mw){}
};int main(array<System::String ^> ^args)
{CBox myBox(2.0, 3.0, 4.0);CGlassBox myCGlassBox(2.0, 3.0, 4.0);myBox.ShowVolume();myCGlassBox.ShowVolume();//我們希望在程序執行時再解決特定的對像實例中使用哪里個Volume()函數的問題,這種操作被稱為動態連接或后期綁定//我們希望ShowVolume()實際調用的Volume()函數版本由被處理的對像種類確定,而不是程序執行之前由編譯器任意確定//我們需要使用的是虛函數system("pause");return 0;
}*///9.6.1 虛函數
//虛函數是以vritual關鍵字聲明的基類函數,如果有基類中將某個函數指定為virtual,并且派生類中有另外一個該函數的定義
//則編譯器將知道我們不想靜態連接該函數,我們真正需要的是基于調用該函數的對像種類,在程序的特定位置選擇調用哪一個函數
/*#include "stdafx.h"
#include <iostream>
using namespace std;
using namespace System;
class CBox
{
public:void ShowVolume(){cout<<"CBox usable volume is "<<Volume()<<endl;}//定義為虛函數virtual double Volume(){return m_Height * m_Length * m_Width;}CBox(double mh=1.0, double ml=1.0, double mw=1.0):m_Height(mh), m_Length(ml), m_Width(mw){}protected://任何派生類的成員函數都可以訪問它們double m_Height;double m_Length;double m_Width;
};class CGlassBox: public CBox
{
public://定義為虛函數virtual double Volume(){return 0.85 * m_Length * m_Width * m_Height;}//注意,雖然我們在派生類的Volume()定義中使用了virtual關鍵字,但這樣做不是必需的,在基類中將該函數定義為virtual已經足夠了//建議在派生類中為虛函數指定virtual關鍵字,因為這樣可以使閱讀派生類定義的任何人都清楚地知道這些函數是動態選擇的虛函數CGlassBox(double mh, double ml, double mw):CBox(mh, ml, mw){}
};
int main(array<System::String ^> ^args)
{CBox myBox(2.0, 3.0, 4.0);CGlassBox myCGlassBox(2.0, 3.0, 4.0);myBox.ShowVolume();myCGlassBox.ShowVolume();system("pause");return 0;
}*/
//要使用某個函數的表現出虛函數的行為,該函數在任何派生類中都必須有與基類函數相同的名稱,形參數和返回類型,如果基類是const,
//那么派生類中的函數也必須是const,如果我們企圖使用不同形參或返回類型,或者將一個聲明為const,而另一個不然,由虛函數機制將不能工作
//注意:
//從派生類函數觀點來看,CGlassBox派生類中的Volume()函數實際上隱藏了該函數的基類版本
//如果我們希望從某個派生類函數中調用基類的Volume()函數,由需要以CBox::Volume()這樣的形式使用作用域解析運算符來引用基類函數//9.6.2 使用指向類對像的指針
//使用指針處理基類和派生類的對像是一項重要的技術,指向基類對像的指針不僅可以包含基類對像的地址,還可以包含派生類對像的地址
//因此,我們可以根據被指向的對像種類,使用類型為"基類指針"的指針獲得虛函數的不同行為
/*#include "stdafx.h"
#include <iostream>
using namespace std;
using namespace System;
class CBox
{
public:void ShowVolume(){cout<<"CBox usable volume is "<<Volume()<<endl;}//定義為虛函數virtual double Volume(){return m_Height * m_Length * m_Width;}CBox(double mh=1.0, double ml=1.0, double mw=1.0):m_Height(mh), m_Length(ml), m_Width(mw){}protected://任何派生類的成員函數都可以訪問它們double m_Height;double m_Length;double m_Width;
};class CGlassBox: public CBox
{
public://定義為虛函數virtual double Volume(){return 0.85 * m_Length * m_Width * m_Height;}//注意,雖然我們在派生類的Volume()定義中使用了virtual關鍵字,但這樣做不是必需的,在基類中將該函數定義為virtual已經足夠了//建議在派生類中為虛函數指定virtual關鍵字,因為這樣可以使閱讀派生類定義的任何人都清楚地知道這些函數是動態選擇的虛函數CGlassBox(double mh, double ml, double mw):CBox(mh, ml, mw){}
};
int main(array<System::String ^> ^args)
{CBox* pCbox;CBox myBox(2.0, 3.0, 4.0);pCbox = &myBox;cout<<"myBox.Volume:";pCbox->ShowVolume();CGlassBox myCGlassBox(2.0, 3.0, 4.0);pCbox = &myCGlassBox;cout<<"CGlassBox.Volume:";pCbox->ShowVolume();//結論:虛函數機制借助于指向基類的指針同樣能夠正常工作,實際調用的函數是基于被指向的對像類型而選擇的//這意味著即使我們不知道程序中某個基類指針所指對像的準確類型(比如某個指針作為實參傳遞給函數時)//虛函數機制也能確保調用正確的函數,這是一種特別強在的功能,因此務必充分理解system("pause");return 0;
}*///9.6.3 使用引用處理虛函數
//如果我們定義一個形參為基類引用的函數,由可以給該函數傳遞派生類的對像作為實參,該函數在執行的時候,將自動為傳遞進來的對像選擇造當的虛函數
/*#include "stdafx.h"
#include <iostream>
using namespace std;
using namespace System;
class CBox
{
public:void ShowVolume(){cout<<"CBox usable volume is "<<Volume()<<endl;}//定義為虛函數virtual double Volume(){return m_Height * m_Length * m_Width;}CBox(double mh=1.0, double ml=1.0, double mw=1.0):m_Height(mh), m_Length(ml), m_Width(mw){}protected://任何派生類的成員函數都可以訪問它們double m_Height;double m_Length;double m_Width;
};class CGlassBox: public CBox
{
public://定義為虛函數virtual double Volume(){return 0.85 * m_Length * m_Width * m_Height;}//注意,雖然我們在派生類的Volume()定義中使用了virtual關鍵字,但這樣做不是必需的,在基類中將該函數定義為virtual已經足夠了//建議在派生類中為虛函數指定virtual關鍵字,因為這樣可以使閱讀派生類定義的任何人都清楚地知道這些函數是動態選擇的虛函數CGlassBox(double mh, double ml, double mw):CBox(mh, ml, mw){}
};void Output(CBox& aBox);int main(array<System::String ^> ^args)
{CBox myBox(2.0,3.0,4.0);CGlassBox myGlassBox(2.0,3.0,4.0);Output(myBox);Output(myGlassBox);system("pause");return 0;
}
//class CBox;
//該語句只是將名稱CBox標識為此該還沒有被定義的類,但已經足以使編譯器知道CBox是個類名,同時使編譯器能夠了處理Output()函數的原型,
//如果不指出CBox是一個類,那么Output()的原型聲明將導致編譯器生成一條出錯消息
void Output(CBox& aBox)
{aBox.ShowVolume();
}
*///9.6.4 純虛函數
/*#include "stdafx.h"
#include <iostream>
using namespace std;
using namespace System;
const double PI = 3.14159265;
//定義純虛函數
class CContainer
{
public:virtual double Volume() const = 0;//定義虛函數Volume()的語句通過在函數頭中添加等號和0,將該函數定義成沒有任何內容,這樣的函數被稱為純虛函數virtual void ShowVolume() const{cout<<endl<<"Volume is"<<Volume()<<endl;}
};//9.6.5 抽像類
//包含純虛函數的類稱為抽像類,因為我們不能定義包含純虛函數的類的對像,抽像類的存在唯一用途,就是定義派生類,
//如果抽像類的派生類將基類的純虛函數依然定義為純虛函數,則該派生類也是抽像類
//純虛函數是否存在是判斷給定的類是否是抽像類的唯一條件
//同樣,抽像類可以擁有多個純虛函數,這種情況下,派生類必須給出基類中每個線純虛函數的定義,否則將仍然是抽像類class CBox : public CContainer
{
public:virtual void ShowVolume() const{cout<<endl<<"CBox usable volume is "<<Volume();}virtual double Volume() const{return m_Width * m_Height * m_Length;}CBox(double lv=1.0, double wv=1.0, double hv=1.0):m_Length(lv), m_Width(wv), m_Height(hv){}
private:double m_Width;double m_Height;double m_Length;
};//CCan類-它表示啤酒或可樂罐
//ectern const double PI;
class CCan: public CContainer
{
public:virtual double Volume() const{return 0.25*PI*m_Diameter*m_Height;}CCan(double hv=4.0, double dv=2.0):m_Height(hv), m_Diameter(dv){}
private:double m_Height;double m_Diameter;
};int main(array<System::String ^> ^args)
{CContainer* pC1 = new CBox(2.0,3.0, 4.0);CContainer* pC2 = new CCan(6.5, 3.0);pC1->ShowVolume();pC2->ShowVolume();delete pC1;delete pC2;//由于派生類對像是動態創建的,因此我們不需要它們時必須使用delete運算符清理自由存儲器system("pause");return 0;
}*///9.6.6 間接基類 子類的基類可以是多另外一個基類派生出來的
/*#include "stdafx.h"
#include <iostream>
using namespace std;
using namespace System;
const double PI = 3.14159265;
//定義純虛函數
class CContainer
{
public:virtual double Volume() const = 0;//定義虛函數Volume()的語句通過在函數頭中添加等號和0,將該函數定義成沒有任何內容,這樣的函數被稱為純虛函數virtual void ShowVolume() const{cout<<endl<<"Volume is"<<Volume()<<endl;}
};
class CBox : public CContainer
{
public:virtual void ShowVolume() const{cout<<endl<<"CBox usable volume is "<<Volume();}virtual double Volume() const{return m_Width * m_Height * m_Length;}CBox(double lv=1.0, double wv=1.0, double hv=1.0):m_Length(lv), m_Width(wv), m_Height(hv){}
protected:double m_Width;double m_Height;double m_Length;
};class CGlassBox : public CBox
{
public:virtual double Volume() const{return 0.85 * m_Width * m_Height * m_Length;}CGlassBox(double lv, double wv, double hv):CBox(lv, wv, hv){}
private:
};//CCan類-它表示啤酒或可樂罐
//ectern const double PI;
class CCan: public CContainer
{
public:virtual double Volume() const{return 0.25*PI*m_Diameter*m_Height;}CCan(double hv=4.0, double dv=2.0):m_Height(hv), m_Diameter(dv){}
private:double m_Height;double m_Diameter;
};int main(array<System::String ^> ^args)
{CContainer* pC1 = new CBox(2.0,3.0, 4.0);CCan myCan(6.5, 3.0);CGlassBox myGlassBox(2.0, 3.0, 4.0);pC1->ShowVolume();delete pC1;pC1 = &myCan;pC1->ShowVolume();pC1 = &myGlassBox;pC1->ShowVolume();//delete pC1; //這里為什么最后不delete pC1了//是因為程序一運行結束,動態內存也就自動釋放了嗎???cout<<endl;//由于派生類對像是動態創建的,因此我們不需要它們時必須使用delete運算符清理自由存儲器system("pause");return 0;
}
//在賦鄧指針另一個地址值之前,我們必須從自由存儲器中刪除CBox對像,如果不這樣做,以后我們將無法清理自由存儲器,因為已經失去了原來對像的地址
//當重新給指針賦值以及使用自由存儲器時,這是個易犯的錯誤
*/
轉載于:https://www.cnblogs.com/xiangxiaodong/archive/2012/11/05/2756174.html
總結
以上是生活随笔為你收集整理的Visual C++ 2008入门经典 第九章类的继承和虚函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ViewVC 1.1.16 发布,CVS
- 下一篇: C#中多线程同步的Monitor理解