《C++程序設計教程》課件第6章_第1頁
《C++程序設計教程》課件第6章_第2頁
《C++程序設計教程》課件第6章_第3頁
《C++程序設計教程》課件第6章_第4頁
《C++程序設計教程》課件第6章_第5頁
已閱讀5頁,還剩156頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

6.1繼承的概念

6.2抽象和分類

6.3繼承的實現(xiàn)

6.4繼承中的成員重定義

6.5繼承下的類域

6.6組合的概念

6.7派生類和組合類對象的構造

6.8模板類的繼承關系

6.9基類和派生類的指針

本章要點

練習

對象就是一件事物,類就是針對一些事物共性的概括。不同的類經(jīng)常具有相似的特征。為了充分利用對象之間的關系,就引進了繼承的概念。繼承(inheritance)是允許通過重用現(xiàn)有類來構建新類的特性。

我們來看一下實際生活中的繼承,如圖6-1所示。假如已經(jīng)定義了“人”,在需要說明什么是“教師”時,只需在“人”的基礎上增加“教書”這一行為即可,即教書的人,而不必從頭說明什么是人。若想進一步說明“男教師”,只需在“教師”的特征中增加“男性”這一特征即可。6.1繼?承?的?概?念圖6-1繼承關系圖

C++中的繼承是在現(xiàn)有的類的基礎上建立一個新類。原類稱為“基類”、“父類”或“超類”,新建類稱為“派生類”或“子類”。子類繼承父類的所有行為和特征。

在圖6-1中的人是學生的基類,學生是大學生的基類?;蛘哒f,學生是人的派生類,大學生是學生的派生類。繼承關系中的大學生有兩個基類,學生是它的直接基類,人是它的間接基類。在對事物的描述中,正確地使用繼承與組合能帶來層次分明、結構清晰的效果,這就要求我們對具體問題進行具體分析和研究,進而進行抽象和分類。

抽象是要找出事物的共同特性和方法,是具體事物描述的一個概括。抽象的目的就是能實現(xiàn)層層繼承。分類就是要找出事物實體的各自特性和方法。它使概念逐漸細化,即具體化。分類的目的就是能實現(xiàn)對類的正確描述。下面通過圖6-2中對交通工具的描述,來表現(xiàn)抽象和分類的關系。其中,從上向下是分類的過程,從下往上是抽象的過程。6.2抽?象?和?分?類圖6-2抽象和分類的關系示意從分類的角度看,交通工具類可分為輪船、飛機和車,車又分為汽車和人力車。其中汽車的行為是可運輸,特征是燃油和有車輪。汽車又可分為卡車和小汽車等??ㄜ嚨奶卣魇禽d貨,小汽車的特征是載人。

從抽象的角度看,在對小轎車類的描述中,展現(xiàn)出所有小轎車都有一個共性:體積小,可載人。對小轎車進行抽象可得到:小轎車是小汽車中的一種,它可以繼承小汽車,它自身的特征是比小汽車更舒適、速度更快等。面向對象的程序設計中,對象被抽象成類,類又是層層分解的,這些類與派生類的關系可以被規(guī)格化描述。描述類后再描述其派生類,就可以只描述其增加部分。所有派生類層次上的編程,都只需在已有類的基礎上進行。派生類繼承基類定義的所有成員并添加自己的獨特成員從而形成一個新類,這樣使得各類之間既有共性又有個性,使各類看似相似卻又是各個獨立的類。

進行抽象和分類的方法主要從行為和特性兩方面考慮,從中找出個性與共性,以及它們之間的關系。這部分工作的好壞直接關系到程序的優(yōu)劣,因而要求在實踐中不斷總結和積累經(jīng)驗。6.3.1建立繼承的方法

在繼承關系中,派生類繼承基類定義的所有成員并添加自己的獨有成員。通過繼承可以實現(xiàn)類庫的重用性,有效地利用現(xiàn)有的類,既使程序結構合理、層次清晰,對對象的描述符合實際,又可縮短程序的開發(fā)時間。

建立繼承的方法是:

class派生類名:(冒號)引用權限類派生表6.3繼?承?的?實?現(xiàn)其中,引用權限有public、protected和private。若不指定引用權限,則為默認的方式,即private。類派生表就是指定的基類。指定的基類必須首先定義,方可被指定。

如對于上面提到的小轎車繼承小汽車,可以用下面的方法實現(xiàn)。先描述基類(小汽車類):

classCar //小汽車類

{protected:

intsize;

public:

Car();

voidTake(); //乘坐

};然后描述派生類,并建立繼承關系。在描述派生類中只要添加新增部分:

classMinbus:publicCar //小轎車,公有繼承小汽車類

{protected:

intspeed;

public:

Minbus();

voidShow(); //作秀

};上面完成的兩個步驟,就實現(xiàn)了圖6-3所示的繼承關系。在聲明派生類Minbus類時指定一個Car類為它的基類,于是小轎車繼承了小汽車,或者說小轎車類是小汽車的派生類。有了這個繼承關系,小轎車就具有了小汽車的所有行為和特征。通過上面的描述,當我們要說什么是小轎車時,不用先說什么是小汽車,而只需說小轎車就是小汽車加上漂亮和快速。通過繼承,我們可以實現(xiàn)一個類對另一個類的特征和方法的利用,C++中稱之為可重用性。因此我們在進行抽象與分類時要考慮到信息的重用和擴展。重用現(xiàn)有的代碼可節(jié)約時間和資金,并提高可讀性。擴展就是增加了新的內容。

繼承可以使已存在的類不需修改地適應新應用。從一定意義上講,繼承是C++面向對象編程的一個基石,是C++的精華部分。如果不存在繼承,各種對象就是相互獨立的,就不能通過類來充分而簡略地描述各種對象。圖6-3繼承示意繼承可以使已存在的類不需修改地適應新應用。從一定意義上講,繼承是C++面向對象編程的一個基石,是C++的精華部分。如果不存在繼承,各種對象就是相互獨立的,就不能通過類來充分而簡略地描述各種對象。

繼承關系必須合理,符合實際生活要求,即符合客觀實際,否則將鬧出笑話且無法完成程序設計。良好的繼承關系,必須經(jīng)過細致分析,使其層次分明、結構合理。要達到該要求,就要進行抽象和分類。在面向對象應用程序開發(fā)中,抽象和分類是十分重要的環(huán)節(jié),也是衡量程序優(yōu)劣的重要標準。6.3.2成員之間的關系

在繼承中基類對象與派生類對象之間存在著既有聯(lián)系,又相互獨立的關系。獨立性表現(xiàn)為,基類對象與派生類對象是兩個不同的對象。聯(lián)系性表現(xiàn)為,基類的所有非靜態(tài)數(shù)據(jù)成員都被拷貝到派生類對象中生成一個副本。也就是說,派生類對象實際上是由子對象,即由基類對象數(shù)據(jù)成員的拷貝和派生類中新增的非靜態(tài)數(shù)據(jù)成員這兩部分組成的?;惖某蓡T函數(shù)不在派生類中作成員函數(shù)的拷貝,基類的成員函數(shù)與派生類共享,反之不成立。

【例6-1】該例說明繼承關系的實現(xiàn)以及成員的訪問方法。

#include<iostream>

#include<string>

usingnamespacestd;

classStudent

{private:

floatcredit; //學分

protected:

floathigh;intweight; //身高、體重public:

stringname; //姓名

Student(string="noname");

voidSet(float,int,float);//設置學分、身高、體重

voidDisplay();

voidCourse(float); //總分

};classGraduateStudent:publicStudent

{protected:

floatsum;

//總學分

public:

GraduateStudent(float=5.0);

voidAdvance(float,float); //累積學分

voidShow(); //得到學分

};Student::Student(strings)

{name=s;high=0;weight=0;credit=0;}

voidStudent::Set(floath,intw,floatc)

{high=h;weight=w;credit=c;}

voidStudent::Display()

{cout<<"name\t"<<"high\t"<<"weight\t"<<"credit\t"<<endl;

cout<<name<<'\t'<<high<<'\t'<<weight<<'\t'<<credit<<endl;

}

voidStudent::Course(floatx){credit+=x;}GraduateStudent::GraduateStudent(floatave){sum=ave;}

voidGraduateStudent::Advance(floatgrade,floatresult)

{sum+=grade*result;}

voidGraduateStudent::Show()

{cout<<"name\t"<<"high\t"<<"weight\t"<<"sum\t"<<endl;

cout<<name<<'\t'<<high<<'\t'<<weight<<'\t'<<sum<<endl;

}voidmain()

{GraduateStudentgs; //定義派生類對象gs

gs.Set(1.82f,65,4.3f); //設置credit、high和weight

gs.Display(); //調用基類的成員函數(shù)

gs.Advance(3,0.8f); //調用派生類的成員函數(shù)

gs.Show(); //調用派生類的成員函數(shù)

Studentstud("Jones");

}運行結果:

name high weight credit

noname 1.82 65 4.3 //學生的姓名、身高和學分

name high weight sum

noname 1.82 65 7.4 //研究生的姓名、身高和

學分

在本例中我們定義了學生類對象stud和研究生類對象gs。gs是學生類的公有派生類對象,它繼承了基類學生類。派生類對象gs實際上是由兩個子對象,即基類中的被繼承部分和派生類中新增的部分組成的。繼承關系中各成員之間的關系如圖6-4所示。圖6-4成員函數(shù)與數(shù)據(jù)成員的關系圖6-4描述了繼承關系中對象stud和對象gs在內存中的分配情況。它共有四個部分:基類對象stud;派生類對象gs;基類的成員函數(shù);派生類中新增的成員函數(shù)。

派生類對象gs由基類對象數(shù)據(jù)成員和派生類對象中新增的非靜態(tài)數(shù)據(jù)組成,基類的成員函數(shù)為基類和派生類共享,這也就是說派生類中同時也擁有了基類的成員函數(shù)。而派生類中的成員函數(shù)僅派生類獨享,基類不能擁有它。

stud對象與gs對象是兩個獨立的對象。gs繼承了stud,因此在gs中擁有了一份基類對象數(shù)據(jù)成員的拷貝。stud中的name等和gs中的name等分別屬于不同的對象。

綜上所述,基類的非靜態(tài)數(shù)據(jù)成員在派生類對象中都有一份拷貝,生成副本,即派生類中包含基類對象數(shù)據(jù)成員。基類的成員函數(shù)不在派生類中生成副本,它們是共享的。也就是說派生類的公有接口,由基類的公有接口加派生類的公有接口組成。6.3.3訪問成員的方法

在繼承關系中訪問成員,可以通過基類成員函數(shù)和派生類成員函數(shù)來實現(xiàn),兩者訪問的成員有所不同。

1.基類成員函數(shù)

在繼承關系中,基類的成員函數(shù)為派生類共享,它能訪問基類的數(shù)據(jù)成員和被拷貝到派生類中的數(shù)據(jù)成員。為了能區(qū)分它們各自訪問的數(shù)據(jù)成員,就要有對象名作引導。在例6-1中,用研究生類對象gs引導調用了基類成員,實現(xiàn)了對派生類數(shù)據(jù)成員的操作。例6-1中的gs.Set(4.3f,1.82f,65)只能訪問圖6-4所示的派生類對象gs中的數(shù)據(jù)成員,若要訪問基類對象中的數(shù)據(jù)成員,就要定義基類對象stud,然后由stud引導調用基類成員函數(shù)。也可以將例6-1主函數(shù)改寫如下:

voidmain()

{GraduateStudentgs; //定義派生類對象gs

gs.Set(4.3f,1.82f,65);

//調用基類的成員函數(shù),設置派生類對象

gs.Display();

//調用基類的成員函數(shù),顯示派生類對象

gs.Advance(3,0.8f); //調用派生類的成員函數(shù),累積學分

gs.Show(); //調用派生類的成員函數(shù),顯示派生類對象

Studentstud(“Jones”); //定義基類對象stud

stud.Set(4.5f,1.78f,63);

//調用基類的成員函數(shù),設置基類對象

stud.Display(); //調用基類的成員函數(shù),顯示基類對象

}運行結果:

name

high

weight credit

noname1.82

65 4.3

//研究生的姓名、身高和學分

name high weight sum

noname 1.82 65 7.4

name high weight credit

Jones

1.78 63 4.5

//學生的姓名、身高和學分從運行結果可見,同樣的Set和Display函數(shù)訪問的數(shù)據(jù)成員是不同的,它通過對象名來區(qū)分。

研究生類中得到了其基類(學生類)中數(shù)據(jù)成員的拷貝。通過派生類對象gs引導,既可調用基類(學生類的成員函數(shù)訪問被拷貝為派生類中的基類數(shù)據(jù)成員),也可調用派生類(研究生類的成員函數(shù)訪問新增數(shù)據(jù)成員),即研究生既有學生的行為也有研究生的行為。

2.派生類成員函數(shù)

派生類成員函數(shù)可以訪問被繼承到了派生類中的原基類的公有和保護成員,但是不可以訪問被繼承到了派生類中的基類的私有成員。例如,派生類的成員函數(shù)Show()不能訪問在圖6-4中派生類的credit數(shù)據(jù)成員,只有基類的成員函數(shù)才可訪問。派生類成員函數(shù)可以訪問派生類的數(shù)據(jù)成員(包括被繼承和新增的數(shù)據(jù)成員)。

3.單繼承中的訪問控制

在繼承關系中的訪問控制如圖6-5所示。該圖表示出了對于基類而言的一般用戶、派生類的成員和友元以及自身的成員和友元的訪問權限;同時也指出,若要想使得派生類的成員函數(shù)也能訪問到基類的私有成員,可以將派生類聲明為基類的友類。圖6-5基類成員在派生類中的訪問控制6.4.1概念與方法

在繼承中,需要新增的行為可以通過在派生類中添加成員函數(shù)來實現(xiàn)。在派生類中若新增的函數(shù)與基類中的成員函數(shù)同名,這種現(xiàn)象就稱為成員函數(shù)的重定義。成員函數(shù)的重定義是指在指定語義上重新修改繼承成員函數(shù)的實現(xiàn)。在派生類中重定義成員函數(shù)之后,基類中的同名函數(shù)就被屏蔽了。

在例6-1中,基類Student中有一個Display函數(shù),現(xiàn)在要在派生類GraduateStudent中添加一個和基類相同的函數(shù)Display,見例6-2。6.4繼承中的成員重定義

【例6-2】演示不改變基類代碼,在派生類中通過重定義為該函數(shù)賦予新的功能。

voidGraduateStudent::Display()

{cout<<“achievementis:”<<sum<<endl;}

那么在主函數(shù)不變的情況下,運行結果為:

achievementis:5 //初始化值

name

high

weight sum

noname 1.82

45 7.4

//研究生的姓名、身高和學分從運行結果來看,gs.Display();調用的是派生類對象研究生的成員函數(shù)Display。我們并沒有改變基類代碼,而是在派生類中通過重定義使該函數(shù)實現(xiàn)新的功能,也就改變了運行結果。它也給了我們一個啟發(fā),假定我們不再需要基類的Display,可在派生類中將它重

定義為空函數(shù)。這樣,用派生類名引導的gs.Display()就訪問不到基類的Display了,這相當于把它屏蔽掉了,即在派生類中一旦賦予了與基類同名的函數(shù)新的功能,該基類函數(shù)就被屏蔽。那么當我們不希望基類中的某函數(shù)被用戶訪問時,能否不賦予某功能而將其屏蔽呢?

【例6-3】演示通過在私有區(qū)重定義空函數(shù)來實現(xiàn)屏蔽。若要在派生關系中實現(xiàn)屏蔽,只將要屏蔽的基類成員在派生類的私有區(qū)重定義便可。

#include<iostream>

usingnamespacestd;

classBaseone

{protected:

intone;

public:

voidDisplay()

{cout<<"Displayone"<<endl;}

};classBasetwo:publicBaseone //公有繼承

{private:

voidDisplay(){} //在私有區(qū)重定義,屏蔽

public:

voidShow()

{cout<<"Displaytwo"<<endl;}

};

voidmain()

{Basetwobtwo;btwo.Show(); //輸出Displaytwo

//btwo.Display(); //非法

}此處,用btwo.Display()訪問就會出錯,也不能再去調用基類的Display,仿佛不存在Display一樣。假如去掉派生類中的重定義,就能調用基類的Display。

繼承中可以通過成員的重定義來實現(xiàn)訪問控制或賦予函數(shù)新的功能。有了重定義,我們就可以達到兩個目的:第一,可以不改變基類,而在派生類中通過重定義為該函數(shù)賦予新的功能;第二,對于不需要繼承的功能,可以通過重定義成員函數(shù)予以屏蔽。6.4.2成員重定義后的訪問

在派生類對象中,雖然有基類的私有成員的拷貝,但派生類成員函數(shù)不能訪問基類的私有成員,只有基類成員函數(shù)才可訪問。在此我們介紹如何在派生類成員函數(shù)內部和外部,訪問保護或公有的基類數(shù)據(jù)成員。

派生類中的被繼承保護或公有成員有兩種情況:一種是未作任何處理的;另一種是被重定義而屏蔽,即不可見的。前者用對象名引導訪問;后者要用基類名來區(qū)分:基類名::成員名。

【例6-4】通過下面的例子介紹如何訪問被屏蔽的基類中的保護或公有成員。

在本例中,基類Student類中有公有數(shù)據(jù)成員credit,在派生類中又對它作了重定義,因而被屏蔽?;愔械墓谐蓡T函數(shù)Display在派生類中也作了重定義,也被屏蔽。這里介紹兩種方法,即在派生類的成員函數(shù)內和成員函數(shù)外的訪問方法。

派生類的Display中提供了在成員函數(shù)中訪問被屏蔽的基類的公有數(shù)據(jù)成員credit的方法。在main函數(shù)中提供了在非成員函數(shù)中訪問被屏蔽的基類的公有數(shù)據(jù)成員credit和成員函數(shù)Display的方法。#include<iostream>

#include<string>

usingnamespacestd;

classStudent

{private:

inthigh; //身高

floatweight;

//體重public:

intcredit; //學分,公有

stringname; //姓名,公有

Student(){name="noname";weight=0;high=0;credit=0;}

voidDisplay(){cout<<"high"<<high<<"weight"<<weight<<endl;}

};classGraduateStudent:publicStudent

{protected:

floatsum; //總學分

public:

intcredit; //學分,數(shù)據(jù)成員重定義

GraduateStudent(){sum=0;credit=0;}

voidAdvance(intgrade,floatresult)

//累積學分

{sum+=grade*result;}

voidDisplay() //成員函數(shù)重定義 {cout<<"sumis:"<<sum<<endl;

cout<<"Studentcreditis"<<Student::credit<<endl; //在成員函數(shù)中的訪問法

cout<<"GraduateStudentcreditis"<<credit<<endl;

}

};

voidmain()

{GraduateStudenta;a.credit=10; //GraduateStudent::credit

a.Student::credit=5; //Student::credit

=“Jonas”; //Student::name

a.Advance(3,0.8f);

cout<<“GraduateStudent:”<<<<endl;

a.Display(); //調用研究生的Display

cout<<“Student:”<<a.Student::name<<endl;

//它未被屏蔽,用結果不變

a.Student::Display(); //調用學生的Display,使被屏蔽者重現(xiàn)

}運行結果:

GraduateStudent:Jonas

sumis:2.4

Studentcreditis5

GraduateStudentcreditis10

Student:Jonas

high0weight0

被繼承部分highweightcredit被屏蔽name新增部分sumcredit重定義運行結果:

GraduateStudent:Jonas

sumis:2.4

Studentcreditis5

GraduateStudentcreditis10

Student:Jonas

high0weight0

本例中成員的關系如圖6-6所示。在派生類中重定義了credit和Display,因此就將原繼承到的credit和Display屏蔽,使之不可見。用a.credit和a.Display()都是訪問重定義的成員,若要訪問被屏蔽的成員就要用類名來引導,如a.Student::credit和a.Student::Display()。圖6-6成員的關系若在派生類成員函數(shù)內訪問數(shù)據(jù)成員,就不需要用對象名來引導。例如在派生類成員函數(shù)Display內訪問重定義的成員credit,可以直接用credit或用GraduateStudent::credit,訪問被屏蔽的成員可用Student::credit。在繼承下,派生類域被嵌套在直接基類域中,如圖6-7所示。在這種嵌套情況下,若一個名字在派生類域中沒有找到,則編譯器會在基類域中查找該名字的定義。6.5繼承下的類域圖6-7類域關系6.5.1重定義與重載的區(qū)別

需要說明的是,成員的重定義和重載是兩個不同的概念。其區(qū)別主要表現(xiàn)在以下幾個方面:

(1)方法不同。重載是定義同名不同參數(shù)函數(shù),其中包括參數(shù)個數(shù)、類型或順序不同;重定義是在派生類中重新定義一個同名函數(shù)。

(2)產(chǎn)生的條件不同。重載可以在普通函數(shù)之間發(fā)生,重定義必須在繼承關系中有效。重載的名字只能在同一類域中并且可見才能生效,而重定義只能在派生類中有效。一般來說,同一作用域中可實現(xiàn)重載,嵌套的作用域中可實現(xiàn)重定義。重載,只能是函數(shù)的重載。重定義不僅可以作用于函數(shù),而且可以作用于數(shù)據(jù)成員。

(3)作用方式不同。重載是在同一作用域中相同的可見函數(shù)名之間發(fā)生,而重定義是在嵌套的作用域中的某一名字將與其同名成員屏蔽而不可見。

(4)執(zhí)行過程不同,函數(shù)匹配關系不同。重載只在當前域中尋找函數(shù)匹配,若不存在該函數(shù),則出錯。重定義時首先在當前類域中選擇同名的函數(shù)匹配,若不存在該函數(shù),則在外層域,如基類域中尋找函數(shù)匹配。若都不存在該函數(shù),則出錯。

【例6-5】演示函數(shù)重定義與重載的區(qū)別。

#include<iostream>

usingnamespacestd;

classStudent

{private:

floatno; //學號

public:

Student(floatx=0){no=x;}

voidDisplay()

{cout<<"no="<<no<<endl;}

};classGraduateStudent:publicStudent

{protected:

floatscore; //平均學分,數(shù)據(jù)成員重定義

public:

??GraduateStudent(floatave=0.0)

{score=ave;}

voidDisplay(intx)

{cout<<"score="<<score<<endl;}};

voidmain()

{GraduateStudenta; //定義派生類對象a

a.Display(); //出錯,不能匹配基類的Display

a.Display(66); //調用派生類的Display

}觀察本例的代碼,研究生類繼承學生類,學生類中有成員Display函數(shù),研究生類中也有Display函數(shù),這就是重定義。即只要研究生類中有同名的Display函數(shù),不論它有幾個或沒有參數(shù),都是重定義,不是重載。若在研究生類中同時有Display(intx)和Display(),則這兩個函數(shù)是重載。

假如去掉派生類中的Display(),那么派生類中的Display(intx)與基類中的Display()也是不能重載的。假如此時對于派生類的對象a發(fā)生a.Display();的調用,就會發(fā)生少給出一個參數(shù)的錯誤。這也就是我們?yōu)槭裁匆Q其為重定義(overriding),而不是重載(overloading)的原因。6.5.2用using消除屏蔽

派生類域被嵌套在直接基類域中,因此基類的Display是被重定義而不可見的,通過使用using可以將已被屏蔽的名字可見??稍诶?-4中添加usingStudent::Display;,使其可見,從而實現(xiàn)重載。…

public:

usingStudent::Display;

voidmain()

{GraduateStudenta; //定義派生類對象a

a.Display(); //ok,匹配基類的Display

a.Display(66); //調用派生類的Display

}一個類以另一個類的對象、引用或指針作為其數(shù)據(jù)成員,我們稱之為組合(composition)類。通過組合可將其他類的特征融入本類的特征中。

在組合關系中常見的方式有兩種:按值組合和按引用組合。按值組合就是類的實際對象被聲明為一個成員。如我們前面的例題中常出現(xiàn)的strings就是按值組合的一個事例。按引用組合就是通過對象的引用或指針間接操作一個對象。6.6組?合?的?概?念若有人類、教師類,又有教具類。教師上課時可以使用教具,也就是說教師有教具。我們可以將教具類作為教師類的數(shù)據(jù)成員。教師是人,教師類可繼承人類。例如:

classTeachTool

{protected:

intsize;

floatweight;

public:

voidhelp();

};classTeacher:publicman //教師類繼承了人類

{protected:

charname[40];

TeachTooltool; //教師類組合了教具類

public:

voidTeaching();

};這樣我們將教具類的特征融入了教師類中,那么教師也有了教具的特征,可以表現(xiàn)為教具的方式,如通過形體語言來提高教學效果。

在繼承關系中我們要求,各繼承關系要符合客觀實際,同樣在組合中也要求組合關系必須符合客觀實際。為了能使關系的描述也貼近實際,在繼承與組合中,用“是”與“有”來描述。在這個組合中我們可以用“有”的關系來描述,我們稱教師“有”教具。要注意,是教師類組合了教具類,而不是教具類組合了教師類,不能說教具“有”教師。因此,類以另一個類對象作為數(shù)據(jù)成員,稱為組合。組合關系就是“有”的關系。6.6.1描述關系

建立了繼承與組合的概念之后,我們自然而然想到,在什么情況下用繼承,什么情況下用組合。若能用“有”和“是”的關系來描述事物,問題就解決了。如現(xiàn)在有三個類:教師類、教具類和人類。教師屬于人的范疇,則可用教師類繼承人類。描述方法就是,教師“是”人。教師使用教具,則可將教具類組合到教師類。描述方法就是,教師“有”教具。反之說教師類繼承教具類,或者說教師類組合人類,都是錯誤的。教師不是教具,教師中有人,難到還有非人教師嗎?若將人類組合教師類,就是說人都有教師特征,也不行。通過上面的分析,我們說,只有當繼承與組合的關系描述符合客觀實際時,才是正確的。也就是說,用“有”和“是”的關系來描述是合理的,這樣的繼承與組合的關系才有意義。

當然在某些情況下,就要由程序員根據(jù)著重點來區(qū)分。例如有教師類,現(xiàn)要建立一個專家類。專家可以繼承教師,描述為專家是教師;也可以不用繼承的方法,而用組合,把教師類作為專家類的數(shù)據(jù)成員,專家也就有了教師的特性,也就可以說專家中“有”教師。6.6.2數(shù)據(jù)成員關系

一個類可以是另一個類的派生類,也可以將某一類的對象作為自己的數(shù)據(jù)成員,使自己也具有某類的特征。在繼承和組合過程中,基類的成員函數(shù)是共享的,在派生類中將得到基類和組合類的數(shù)據(jù)成員的拷貝。由于在繼承與組合關系中既有共享的成員函數(shù),又有數(shù)據(jù)成員的拷貝,因此我們在掌握它們的調用和訪問方法的同時,也要避免一些特征的重復,如姓名的重復等。下面說明繼承和組合關系同時存在時各成員的訪問方法。

【例6-6】本例說明繼承和組合中數(shù)據(jù)成員的拷貝及訪問方法。

#include<iostream>

#include<string>

usingnamespacestd;

classAdvisor //導師

{protected:

stringname;public:

Advisor(strings="Sandy")

{name=s;}

voidADisplay()

{cout<<"Advisorname"<<name<<endl;} //輸出姓名

};

classStudent{protected:

stringname;

//姓名

public:

Student(strings=“John”)

{name=s;}

voidSetname(strings=“noname”)

{name=s;}

voidDisplay()

{cout<<“Studentname”<<name<<endl;}

//輸出姓名

};classGraduateStudent:publicStudent

//研究生類繼承學生類,組合了導師類

{protected:

Advisoradvisor;

stringname;public:

GraduateStudent(strings="noname")

{name=s;}

voidGsDisplay()

{cout<<"GraduateStudentname"<<name<<endl;

cout<<"Studentname"<<Student::name<<endl;

advisor.ADisplay(); //顯示導師姓名

}

};voidmain()

{Studentst(“Lolee”);

//創(chuàng)建Student對象st

GraduateStudentgs("Jonas");//創(chuàng)建GraduateStudent對象gs

Advisoras("Java"); //創(chuàng)建Advisor對象as

gs.Setname("Huanghua"); //設置Student姓名

st.Display(); //顯示學生姓名Lolee

gs.GsDisplay(); //顯示研究生姓名Jonas和導師姓名Sandy

gs.Display(); //顯示研究生中的學生姓名Huanghua

}運行結果:

StudentnameLolee

GraduateStudentnameJonas

StudentnameHuanghua

AdvisornameSandy

StudentnameHuanghua 本例中,研究生繼承了學生,組合了導師。研究生既有學生行為,也有導師特征。研究生“是”學生,研究生中“有”導師,也就是具有可輔導特征。這是符合常理的。由于導師對象是研究生的保護成員,因此對導師的訪問只能用研究生的成員函數(shù)來實施。

例6-6中各對象的數(shù)據(jù)成員在內存中的空間分配如圖6-8所示。在內存中共開辟有三個空間,它們分屬于st、as和gs三個對象。用研究生對象作引導的各個顯示函數(shù)都作用在gs空間的數(shù)據(jù)成員上。在研究生中有學生、導師和自己三個姓名,這樣的繼承不是一個好方法,因為一個研究生同時有三個名字不合常理。所以說在使用繼承與組合時要考慮到數(shù)據(jù)成員的冗余。圖6-8各對象存儲關系6.7.1概念與方法

在繼承與組合關系中,類的構造次序是:先構造基類,再構造組合類,然后構造派生類。類構造離不開構造函數(shù),那么就必須能使各個構造函數(shù)工作,我們稱之為激活構造函數(shù)。同時,我們也要考慮如何進行參數(shù)傳遞。6.7派生類和組合類對象的構造對于基類的構造可以用默認構造函數(shù)(稱為隱式),也可以指定參數(shù)(稱為顯式)。當需要指定基類構造函數(shù)的參數(shù)時,在派生類構造函數(shù)后加“:基類名(參數(shù)表)”。

對于組合類的構造可以用默認構造函數(shù),也可以指定參數(shù)。當需要指定組合類構造函數(shù)的參數(shù)時,在派生類構造函數(shù)后加“:對象名(參數(shù)表)”。6.7.2構造與激活

在構造派生類時,必須激活基類構造函數(shù)。激活方式可以是直接給出基類參數(shù),由派生類的構造函數(shù)傳出參數(shù)給基類構造函數(shù),也可讓基類以默認參數(shù)構造。

在以前介紹的繼承關系中,未討論如何激活基類構造函數(shù),而都是以默認參數(shù)的方法激活基類構造函數(shù)的。

1.派生類對象的構造

激活基類構造函數(shù)的方法是:在派生類的構造函數(shù)中增加被傳遞的參數(shù),然后在派生類構造函數(shù)形參列表的圓括號后加冒號以及基類名和形參列表?!纠?-7】通過派生類構造函數(shù)傳遞參數(shù)。

#include<iostream>

#include<string>

usingnamespacestd;

classStud

{private:

stringname;

charsex;

protected:

intnum;public:

Stud(intn=982,strings="Wanggang",charc='M')

{name=s;num=n;sex=c;}

voidDisplay()

{cout<<"num:"<<num<<"\t"<<"name:"<<name<<"\t"<<"sex:"<<sex<<endl;}

};

classStudent:publicStud //派生類,派生于Stud{private:

intage;

stringaddr;

public:

Student(intn,stringnam,charc,intag,stringad)

:Stud(n,nam,c)

//在構造基類時用n、nam、c初始化Stud

{age=ag;addr=ad;}

voidShow() {cout<<“num:”<<num<<“\t”; cout<<“age:”<<age<<“\t”<<“address:”<<addr<<endl;

Display(); //調用基類Display

}

};

voidmain()

{Studa;

a.Display();

Studentb(994,“Lolee”,‘W’,26,“shanghai”);

//先構造基類Stud,再構造派生類

b.Show();

b.Display();

}運行結果:

num:982name:Wanggangsex:M

num:994age:26adress:shanghai

num:994name:Loleesex:W

num:994name:Loleesex:W

2.派生類中組合新特征對象的構造

一個類可以將另一個類的對象作為自己的數(shù)據(jù)成員,使自己擁有新特征,派生類也是如此。在構造派生類組合類對象時同樣要求激活組合類構造函數(shù)。對于類中具有常量的數(shù)據(jù)成員,必須初始化。

組合類的激活方式與激活基類的方式相同,若既有繼承又有組合時要用逗號分隔。若在派生類中還有需要初始化的成員,可以再用逗號分隔,然后加上成員名和參數(shù)。在C++中,將構造函數(shù)后的“:”和用“,”分開的成員名及其初始值的列表稱為成員初始化表。成員初始化表只能在構造函數(shù)定義中被指定,而不是在聲明中指定。該表被放在參數(shù)表和構造函數(shù)體之間。

【例6-8】說明如何構造派生類中組合的新成員的對象。在派生類中組合了導師Advisor類,也組合了const的成員ID。對于常量、變量成員,必須在構造時完成初始化。#include<iostream>

#include<string>

usingnamespacestd;

classAdvisor //導師類

{protected:

charsex;public:

Advisor(charadv=‘m’)

{sex=adv;cout<<“ConstructAdvisor\n”;}

voidDisplay()

{cout<<“Advisorsexis”<<sex<<endl;}

};

classStudent

//學生類

{protected:

stringname;public:

Student(strings=“noname”)

{name=s;cout<<“ConstructStudent\n”;}

voidDisplay()

{cout<<“Studentnameis”<<name<<endl;}

};

classGraduateStudent:publicStudent

//研究生類繼承了學生類,組合了導師類

{protected:

Advisoradvisor; //組合了導師類

constintID;

//const的成員

floatsalary;public:

GraduateStudent(strings,charadv,floatsal)

:Student(s),advisor(adv),ID(320)

//多個成員的初始化值之間用逗號分隔

{salary=sal;cout<<"ConstructGraduateStudent\n";}

voidDisplay()

{cout<<"ID="<<ID<<'\t'<<"GraduateStudentnameis"<<name

<<'\t'<<"Salaryis:"<<salary<<endl;

advisor.Display(); //在成員函數(shù)中訪問保護成員advisor

}

};voidmain()

{Advisoras;as.Display();Studentds("Lolee");

ds.Display();cout<<"\n\n";

GraduateStudentgs("Lihua",'w',3500);

gs.Display();

}運行結果:

ConstructAdvisor

Advisorsexism

ConstructStudent

StudentnameisLolee

ConstructStudent

ConstructAdvisor

ConstructGraduateStudent

ID:320GraduateStudentnameisLihuaSalaryis:3500

Advisorsexisw在派生類研究生類GraduateStudent的構造函數(shù)后用:Student(s),意為用s作為參數(shù)去初始化在派生類中得到的一份學生類Student數(shù)據(jù)成員的拷貝。而advisor(adv)意為以adv的值來初始化組合在研究生類中的導師類advisor的對象。

【例6-9】演示在派生類中如何初始化基類中的const數(shù)據(jù)成員。#include<iostream>

#include<string>

usingnamespacestd;

classStudent

//學生類

{protected:

stringname; //姓名

constintid; //const的成員public:

Student(strings=“noname”,intx=738)

//用x初始化基類中的const數(shù)據(jù)成員

:id(x)

{name=s;cout<<"ConstructStudent\n";}

voidDisplay()

{cout<<id<<'\t'<<name<<'\t'<<endl;}

};classGraduateStudent:publicStudent

{protected:

constintID; //const的成員

floatsalary;

stringname; //姓名public:

GraduateStudent(stringss,floatsal,inty,stringsg)

:Student(ss,y),ID(320),salary(sal) //salary的初始化

{name=sg;cout<<"ConstructGraduateStudent\n";}

voidDisplay()

{cout<<"ID="<<ID<<'\t'<<name<<'\t'<<salary<<endl;

cout<<"id="<<id<<'\t'<<Student::name<<'\t'<<salary<<endl;

}

};voidmain()

{Studentds("Lolee");

ds.Display();

cout<<endl;

GraduateStudentgs("Lihua",3500,388,"Jonas");

gs.Display();

}運行結果:

ConstructStudent

738 Lolee

ConstructStudent

ConstructGraduateStudent

ID=320 Jonas 3500

Id=388 Lihua 3500

觀察本例可以發(fā)現(xiàn),在研究生對象的特征中既有學生的姓名,也有研究生的姓名,即一個學生有兩個名字,作為學生是Lihua,作為研究生是Jonas,所以這方法不好??梢园蜒芯可男彰サ簦@個請讀者自己完成。模板是一種高效、安全和實用的重用代碼方式。它實現(xiàn)了類型參數(shù)化,在創(chuàng)建對象或函數(shù)時所傳遞的類型參數(shù)決定了其性質。模板類的派生類與普通類一樣,有公有、保護和私有三種,繼承成員的訪問控制規(guī)則也一樣。

所謂模板,就是一個框架,一個還沒有決定成員中參數(shù)類型的框架。那么在模板類的繼承關系中,不僅要給出派生類中的類型參數(shù),而且要給出基類中的類型參數(shù)。

【例6-10】演示在派生類中給出類型參數(shù)的方法。6.8模板類的繼承關系#include<iostream>

#include<string>

usingnamespacestd;

template<classType>

classBase //定義了類模板,類名是Base

{public:

voidShow(Typeb)

{cout<<b<<"\n";}

};template<classType1,classType2>

classDerived

:publicBase<Type2>

//指定Derived是Base<Type2>的公有派生類

{public:

voidSHow(Type1d1,Type2d2)

{cout<<d1<<""<<d2<<endl;}

};voidmain()

{Derived<char*,float>objA;

objA.SHow("Piis:",3.14159f); //調用成員函數(shù)

Derived<char*,char>objB;

objB.SHow("Areyousure",'?'); //調用成員函數(shù)

objB.Show(89); //給出ASCII碼值

//obj.SHow("Youareright"); //出錯,參數(shù)太少

Derived<string,char>objC;

objC.SHow("Godsaveme",'?');

}運行結果:

Piis:3.14159

Areyousure?

Y

Godsaveme?若在例6-10的派生類Derived中,將SHow函數(shù)名也命名為Show,那么派生類中的SHow就與基類中的Show同名,這就是重定義。在派生類中只有用基類名來引導以示區(qū)分,才能訪問基類中的Show,此時對上例的main函數(shù)修改如下:

voidmain()

{Derived<char*,float>obj;

obj.Show("Piis:",3.14159f); //調用基類成員函數(shù)

Derived<char*,char>OBj;

OBj.Show("Areyousure",'?'); //調用派生類成員函數(shù)

OBj.Base<char>::Show(89); //用類名引導,調用基類成員函數(shù)

}

【例6-11】本例介紹類模板的帶參數(shù)構造函數(shù)的使用以及在派生類中的激活方式。

#include<iostream>

usingnamespacestd;

template<classType>

classBase

//定義了類模板,類名是Base

{protected:

Typeb;

//成員類型由Type決定public:

Base(Typex){b=x;} //帶參數(shù)構造函數(shù)

voidShow(){cout<<b<<“\n”;}

};

template<classType1,classType2>

classDerived

:publicBase<Type2>

//指定Derived是Base<Type2>的公有派生類

{protected:

Type1d;public:

Derived(Type1y,Type2z)

:Base<Type2>(z) //激活基類構造函數(shù),并將z傳給基類

{d=y;}

voidSHow(){cout<<d<<""<<b<<endl;}

};voidmain()

{Base<float>obj1(3.14f); //定義基類對象obj1并用3.14f初始化

obj1.Show(); //調用基類的Show函數(shù)

Derived<char*,double>obj2("Piis:",3.14159);

//構造派生類對象,并激活基類構造函數(shù)

obj2.SHow(); //調用派生類的SHow函數(shù)

}6.9.1使用規(guī)則

在C++中,基類指針可以直接指向公有派生類對象,而派生類指針必須經(jīng)過強制轉換,轉換為基類指針后方可指向基類對象。前者是系統(tǒng)自動進行的隱式轉換,后者是人為的強制轉換。對于強制轉換,使用時要慎重,它可能帶來錯誤結果。6.9基類和派生類的指針6.9.2使用方法

對于用指針訪問對象成員,主要討論的是派生類指針與基類指針交叉使用的情況,進而從中找出一些新特點與應用。

1.用基類指針指向派生類對象

對于基類A、派生類B,我們可用“是”的關系描述為:B“是”A。既然B是A,那么就可以直接用基類指針指向派生類對象。為此C++中提供了自動轉換。但是將基類指針指向派生類對象后,對派生類的訪問是受限制的,它只能調用基類成員函數(shù)并訪問派生類中被繼承的基類成員。若想要調用派生類成員函數(shù),訪問派生類中獨有的成員,就要將該指向派生類對象的基類指針強制轉換為派生類指針。這種用基類指針指向派生類對象,調用派生類和基類成員函數(shù)的方法是安全可行的。該方法是在繼承關系中使指針能發(fā)揮優(yōu)勢的常用方法,將在下一章中詳細介紹。

2.用派生類指針指向基類對象

對于用派生類指針指向基類對象,C++中規(guī)定,必須將派生類指針強制轉換為基類指針,否則將產(chǎn)生語法錯誤。轉換后的指針只能調用基類成員函數(shù),并訪問派生類中的基類成員。也就是說它還是指向派生類,只不過調用范圍被限制,可訪問的數(shù)據(jù)成員也被限制在派生類中的基類成員,沒有實際意義,一旦再想調用派生類成員函數(shù)就出錯。因此,用派生類指針強制轉換后,指向基類對象成員是危險的,也是不可取的。

【例6-12】在本例中,點類是基類,它派生出圓類。在main中可以看到,用基類指針指向派生類對象,訪問了基類成員函數(shù),正確;用基類指針指向基類對象,訪問了派生類成員函數(shù),錯誤。

#include<iostream.h>

classPoint

{friendostream&operator<<(ostream&,constPoint&);

protected:

floatx;floaty;public:

Point(float=0,float=0); //初始化點的位置

voidsetPoint(float,float); //設定點的位置

floatgetX()const{returnx;} //得到x坐標

floatgetY()const{returny;} //得到y(tǒng)坐標

};

Point::Point(floata,floatb){x=a;y=b;}

voidPoint::setPoint(floata,floatb){x=a;y=b;}ostream&operator<<(ostream&output,constPoint&p)

//友元函數(shù)的定義

{output<<'['<<p.x<<","<<p.y<<']';

returnoutput;

}

classCircle:publicPoint

{friendostream&operator<<(ostream&,constCircle&);

protected:

floatradius;public:

Circle(float=0.0,float=0,float=0);

voidsetRadius(float); //設定半徑

floatgetRadius()const; //得到半徑

floatarea()const; //得到面積

};

Circle::Circle(floatr,floata,floatb):Point(a,b)

{radius=r;}voidCircle::setRadius(floatr)

{radius=r;}

floatCircle::getRadius()const

{returnradius;}

floatCircle::area()const

{return3.14159f*radius*radius;}

ostream&operator<<(ostream&output,constCircle&c)

{output<<"Center=["<<c.x<<","<<c.y<<"];Radius="<<c.radius;

returnoutput;

}voidmain()

{Point*pointp,p(3.5f,5.3f);

Circle*circlep,c(2.7f,1.2f,8.9f);

cout<<“Pointp:”<<p<<“\nCirclec:”<<c<<endl;

pointp=&p; //ok,基類指針指向基類對象

pointp=&c; //ok,基類指針指向派生類

cout<<pointp->getX();

//ok,調用基類成員函數(shù),輸出派生類對象中的x為1.2

//cout<<pointp->area();

//error,基類指針不可訪問派生類中的新增成員函數(shù)

circlep=(Circle*)pointp;

//ok,將該基類指針強制轉換為派生類指針

cout<<“\nAreaofcircle:”

<<circlep->area()<<endl;

//ok,經(jīng)轉換后的派生類指針可訪問派生類中的成員

cout<<circlep->getX();

//ok,用派生類指針訪問基類成員函數(shù),輸出x為1.2

cout<<pointp->getX();//ok,基類指針訪問基類成員函數(shù)

//circlep=&p; //error,派生類指針不能直接指向基類對象

pointp=&p; //ok,基類指針指向基類對象

circlep=(Circle*)pointp; //強制轉換

cout<<"\nRadiusofobjectcircle:"<<circlep->getRadius()<<endl;

}運行結果:

Pointp:[3.5,5.3] //初始化的坐標

Circlec:Center=[1.2,8.9];Radius=2.7 //重設置的坐標和半徑

1.2

Areaofc(viacirclePtr):22.9022 //輸出圓面積

1.21.2

RadiusofobjectcirclePtrpointpointto:9.45831e-039

//輸出不確定值●?繼承是在現(xiàn)有的類的基礎上建立一個新類,通過繼承,可以實現(xiàn)類庫的可重用性。

●?類以另一個類的對象作為數(shù)據(jù)成員,稱為組合。

●?在繼承方式中可用“是”來描述,在組合方式中可用“有”來描述。

●?面向對象的程序設計基于的兩個原則就是抽象和分類。

●?抽象是要找出事物的共同的特性和方法,是具體事物描述的一個概括。

●?分類就是要找出事物實體的各自特性和方法,是使概念逐漸細化,即具體化。本章要點●?進行抽象和分類的方法主要從行為和特性兩方面考慮,從中找出共性與個性以及它們之間的關系。

●?在繼承關系中,被繼承的成員函數(shù)是共享的,被繼承的數(shù)據(jù)成員將在派生類中生成一份拷貝。

●?在派生類中若新增函數(shù)與基類中的成員函數(shù)具有相同的原型,這種現(xiàn)象就稱為成員函數(shù)的重定義。

●?在繼承中的重定義可以不改變基類,而在派生類中通過重定義為該函數(shù)賦予新的功能。對于不需要繼承的功能,可以通過重定義成員函數(shù)來予以屏蔽?!?派生類的成員函數(shù)不能訪問基類的私有成員,可以訪問基類的公有或保護成員。

●?在單個類中,private和protected成員沒有什么區(qū)別。但在繼承關系中,基類的private成員不但對應用程序隱藏,甚至對派生類也隱藏。而基類的保護成員則只對應用程序隱藏,對派生類則不隱藏。

●?在派生類中可通過類名引導訪問被屏蔽的基類中的保護或公有成員。

●?若希望從基類繼承的大多數(shù)公有成員變成私有或保護成員,僅其中幾個仍保持原性質,就要用恢復訪問控制方式?!?構造派生類對象時,必須激活所有基類構造函數(shù)。激活時可以向基類給出參數(shù),或由派生類構造函數(shù)傳出參數(shù),也可讓基類以默認參數(shù)構造。

●?對于既是派生類又是組合類的對象,它的構造次序是:先構造基類,再構造組合類,然后構造派生類。

●?類與類之間要分工明確,各做各的,以接口作溝通?!?模板是一種高效、安全和實用的重用代碼方式。在模板類的繼承關系中不僅要給出派生類中的類型參數(shù),而且還要給出基類中的類型參數(shù)。

●?基類指針可以直接指向公有派生類對象,若還想要用該基類指針訪問派生類中新增的成員,就需要強制轉換。

●?把基類指針指向公有派生類對象,再把基類指針強制轉換為派生類指針,就可以方便地訪問派生類對象了。一、概念

1.什么是繼承與組合?說明它們的作用。

2.什么是抽象和分類?

3.在繼承中成員之間的關系是:基類的成員函數(shù)與派生類對象

,基類的非靜態(tài)數(shù)據(jù)成員在派生類對象中則生成一份

。

4.在派生類中,對基類中的成員函數(shù)重定義的目的是:

5.在派生類中,訪問被屏蔽的基類中公有成員函數(shù)的方法是:

。練習

6.在派生類成員函數(shù)中,可以訪問基類對象中的

成員。

7.若不標明繼承是公有、保護或私有,則默認為

繼承。

8.繼承關系中,成員的調整是

訪問控制方式。

9.在繼承與組合關系中,可以用

關系來描述繼承,可以用

關系來描述組合。

10.在C++中,基類指針可以

指向派生類對象,派生類指針

指向基類對象。二、分析

1.請說出下例對象中哪些可以用“是”來描述,哪些可以用“有”來描述。

人、房屋、建筑、門、凳子、兒童、男生、女生、課本

2.用程序語言描述人、男生、女生和課本的繼承與組合關系。

3.說出下列程序的運行結果。

#include<iostream>

usingnamespacestd;

classB

{public: B(int=0,int=0);

voidPrintb();

private:

inta,b;

};

classA

{public:

A();

A(int,int);

voidPrinta();

private:

Bc;

};A::A(inti,intj):c(i,j)

{cout<<"構造類A"<<endl;}

voidA::Printa(){c.Printb();}

B::B(inti,intj)

{cout<<"構造類B"<<endl;

a=i;b=j;

}voidB::Printb()

{cout<<"a="<<a<<",b="<<b<<endl;}

voidmain()

{Am(10,20);

m.Printa();

}

4.下面的程序有錯嗎?說出運行結果。

#include<iostream>

usingnamespacestd;

classStudent

{private:

charname[40];

intnum;

public:

Student(char*pName="noname",intx=201);

voidSet(char*);

voidDisplay();

};classSchoolgirl:publicStudent

{protected:

chartext[40];

floatscore;

public:

Schoolgirl(char*pText="C++",float=60);

voidStudy(float);

};Student::Student(char*pName,intx)

{strcpy(name,pName);

num=x;

}

voidStudent::Set(char*pName)

{strcpy(name,pName);}

voidStudent::Display()

{cout<<"num\t"<<"name\t"<<endl;

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論