




版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
第11章重載與模板編譯時的多態性是通過函數重載和模板體現的。利用函數重載機制,在調用同名的函數時,編譯系統可根據實參的具體情況確定所調用的是同名函數中的哪一個。利用函數模板,編譯系統可根據模板實參以及模板函數實參的具體情況確定所要調用的是哪個函數,并生成相應的函數實例;運算符重載是函數重載的一種特殊情況。利用類模板,編譯系統可根據模板實參的具體情況確定所要定義的是哪個類的對象,并生成相應的類實例。§11.1重載定義
函數重載(functionoverloading)是指一個函數名稱具有不同形式的參數的若干個(兩個以上)函數的定義。注意:重載函數可以具有相同的返回類型.但必須有不同類型的參數列表。用不同返回類型和相同參數表生成重載函數將產生語法錯誤。編譯器只用參數列表的數據類型區別同名函數。重載函數不一定要有相同個數的參數,它們可以有不同個數的參數列表。程序員使用帶默認值參數的重載函數時要小心,以免出現歧義。一、運算符重載引入類的對象(即抽象數據類型的實例)的操作是通過向對象發送消息完成其功能的(即調用成員函數的形式)。對某些類(特別是有些數值計算類)來說,這種調用方式是繁瑣的,而用C++中的豐富的內部運算符來指定對對象的操作會更容易表達和理解。定義把C++中的運算符和類的對象結合在一起形成有意義的表達式,這個過程稱為運算符重載。+-*/%^&|~!=<>+=-=*=/=%=^=&=|=<<>>>>=<<===!=<=>=&&||++--,[]()newnew[]deletedelete[]
C++語言中可以重載的運算符
C++語言中不能被重載的運算符
.->::?:sizeof注意:重載不能改變運算符的優先級。雖然重載具有固定優先級的運算符可能會不便使用,但是在表達式中使用圓括號可以強制改變重載運算符的計算順序。重載不能改變運算符的結合律。重載不能改變運算符操作數的個數。重載的一目運算符仍然是一目運算符,重載的二目運算符仍然是二目運算符,但C++中的唯一的三目運算符(?:)也不能被重載。運算符&、*、+和-既可以用作一元運算符,也可以用作二目運算符,可以分別把他們重載為一目運算符和二目運算符。重載不能創建新的運算符,只有現有的運算符才能被重載。運算符重載不能改變該運算符用于內部類型對象時的含義。例如,程序不能改變運算符+用于兩個整數相加的含義。運算符重載通常和用戶自定義的類類型的對象一起使用,或者類類型的對象和內部類型的對象混合運算。運算符重載函數說明的一般形式如下:<類類型>operator<運算符>(<參數說明>);
運算符重載函數定義的一般形式如下:<類類型><類類型名稱>::operator<運算符>(<參數說明>){<語句序列>}用于類的對象的運算符必須重載,但有兩種情況需要注意:賦值運算符“=”無需重載就可用于每一個類類型。在沒有重載賦值運算符時,賦值運算符的缺省行為是復制對象的數據成員。但是,這種缺省的復制行為用于帶有指針成員的對象時是危險的,該情況通常需要顯式重載賦值運算符。地址運算符&也無需重載就可以用于任何類的對象,它返回對象在內存中的地址。地址運算符也可以被重載。
當一個類重載了賦值運算符=和加法運算符+,下列語句是允許的:
object2=object2+object1;但并不意味運算符+=也被自動重載了。因此,下面的語句是錯誤的:
object2+=object1;然而,顯式地重載運算符+=可使上述語句成立。運算符重載函數既可以是類的成員函數,也可以是非成員函數。非成員函數通常是友元函數。成員函數是用this指針隱式地訪問類的對象參數,非成員函數的調用必須明確地列出類的對象參數。不管運算符重載函數是成員函數還是非成員函數,運算符在表達式中的使用方式是相同的。
運算符重載函數的實現方式通常根據下列情況來選擇:當運算式子的左邊操作數(或者只有左邊的操作數)是定義類的一個對象(或者是對象的引用),運算符重載函數可以實現為定義類的一個成員函數。當左邊的操作數可能是另一個不同類的對象(或者為一個內部類型的操作數),運算符重載函數必須要用一個非成員函數來實現。由于運算符重載函數為類的非成員函數,當需要訪問類的對象的隱藏成員時,則必須指定為該類一個友元函數。【例11.1】實現復數類并重載有關的運算符。//Complex.h-存放類定義的文件classCComplex//定義一個復數類{public: CComplex(doublem_re=0,doublem_im=0);//含有缺省值的構造函數 CComplex(CComplex&x);//復制構造函數 CComplexoperator+(doublex);//實現左邊為該類對象與一個double數相加 CComplexoperator+(CComplex&x);//實現左邊為該類對象與一個該類的對象相加 CComplexoperator-(CComplex&x);//實現左邊為該類對象與一個該類的對象相減 CComplexoperator*(CComplex&x);//實現左邊為該類對象與一個該類的對象相乘 CComplexoperator/(CComplex&x);//實現左邊為該類對象與一個該類的對象相除 voidPrint();private: doublere;//存放實部 doubleim;//存放虛部};//Complex.cpp-存放類定義的實現部分和主函數#include"Complex.h"#include"iostream.h"CComplex::CComplex(doublem_re,doublem_im){ re=m_re; im=m_im;}CComplexCComplex::operator+(doublex){ doubler,i; r=re+x; i=im; CComplexy(r,i); return(y);}CComplexCComplex::operator+(CComplex&x){ doubler,i; r=re+x.re; i=im+x.im; CComplexy(r,i); return(y);}CComplexCComplex::operator-(CComplex&x){ doubler,i; r=re-x.re; i=im-x.im; return(CComplex(r,i));//返回匿名對象}CComplexCComplex::operator*(CComplex&x){ doubler,i; r=re*x.re-im*x.im; i=re*x.im+im*x.re; return(CComplex(r,i));}CComplexCComplex::operator/(CComplex&x){ doubler,i; r=(re*x.re+im*x.im)/(x.re*x.re+x.im*x.im); i=(im*x.re-re*x.im)/(x.re*x.re+x.im*x.im); return(CComplex(r,i));}CComplex::CComplex(CComplex&x){ re=x.re; im=x.im;}voidCComplex::Print(){ cout<<re<<"+"<<im<<"i"<<endl;}voidmain(void){ CComplexa(3,6),b(9,5); a.Print(); b.Print(); CComplexc; c=a+b; c.Print();c=a+6; c.Print();}輸出結果:3+6i↙9+5i↙12+11i↙9+6i↙二、類型轉換在C++語言中,兩個相同的內部數據類型的運算其結果還是這種數據類型,例如,整數加整數還是整數。但是,還有一種需要將一種數據類型的數據轉換為另一種數據類型的數據的運算,如賦值、混合數據類型的計算、函數實參傳遞數值以及函數返回值等等,都可能會發生這種情況。對于內部數據類型的情況,編譯器知道如何隱式地進行類型的轉換,也可以用強制類型轉換運算符來實現內部類型之間的強制轉換。但用戶自定義數據類型轉換,編譯器不知道怎樣實現與其它數據類型之間的轉換,程序必須要明確地指明如何轉換。這種轉換可以用轉換構造函數實現,也可以用單個參數的構造函數實現。這種構造函數僅僅把其他類型(通常是內部數據類型)的對象轉換為某個特定類的對象。
強制類型轉換運算符(簡稱為轉換運算符)可以把一個類的對象轉換為其他類的對象或內部類型的對象。該運算符重載函數必須是一個非static成員函數(不能是友元函數)。例如,下面的強制類型轉換的運算符重載函數的函數原型:
A::operatorchar*()const;
說明了一個強制類型轉換的運算符重載函數,它根據用戶自定義類型A的對象建立一個臨時的char*類型的對象。強制類型轉換的運算符重載函數不能指定返回類型(返回類型是要轉換后的對象類型)。如果s是一個自定義類類型的對象,當編譯器遇到表達式(char*)s時,程序會產生函數調用s.operatorchar*(),操作數s是調用成員函數operatorchar*的類對象s。
為了把用戶自定義類型的對象轉換為內部數據類型的的對象或者為其他用戶自定義數據類型的對象,可以定義強制類型轉換的運算符重載函數。例如,下面給出兩個強制類型轉換的運算符重載函數的函數原型:
A::operatorint()const;A::operatorotherClass()const;其中,第一個強制類型轉換的運算符重載函數用來把用戶自定義類型A的對象轉換為一個整數,第二個強制類型轉換的運算符重載函數轉換為otherClass類型的對象。
強制類型轉換的運算符重載函數一個很好的特點是:當需要的時候,編譯器可以為建立一個臨時對象而自動地調用這個函數。例如,如果用戶自定義的類String的一個對象s出現在程序中需要使用char*類型的對象的位置上:Strings;cout<<s;編譯器調用重載的強制類型轉換運算符函數operatorchar*將對象轉換為char*類型,并在表達式中使用轉換后的char*類型的結果。String類提供該轉換運算符后,不需要重載流插入運算符用cout輸出String。三、特殊運算符的重載增一“++”和減一“—”運算符分為前置和后置的增一及減一運算符,它們都可以被重載。下面將介紹編譯器如何識別前置和后置的增一“++”運算符及減一“—”運算符。重載既能允許前置又能允許后置的增一運算符,每個這種運算符重載函數必須有一個明確的特征以使編譯器能確定要使用哪個“++”版本。前置增一“++”運算符重載函數的方法與重載其他前置一目運算符一樣。
【例11.2】用運算符重載實現CDate類的操作。類CDate中,重載的前置和后置增一運算符將一個CDate對象增加1天,必要時使年、月遞增。類CDate的Public接口提供了以下成員函數:一個重載的流插入運算符,一個默認的構造函數、一個setDate函數、一個重載的前置自增運算符函數、一個重載的后置自增運算符函數、一個重載的加法賦值運算符(+=)、一個檢測閏年的函數和一個判斷是否為每月最后一天的函數。#include<iostream.h>classCDate{ friendostream&operator<<(ostream&output,constCDate&d);public:CDate(intm=1,intd=1,inty=1900);voidsetDate(int,int,int); //設定日期CDate&operator++(); //前置自增運算符CDateoperator++(int); //后置自增運算符constCDate&operator+=(int);//增加指定天數到當前日期上boolleapYear(int); //是否閏年 boolendOfMonth(int); //是否為每月最后一天private: intmonth,day,year; staticconstintdays[]; //每月天數 voidhelpIncrement(); //計算日期};//date.cpp-CDate類的成員函數定義和主函數定義#include"date.h"constintCDate::days[]={0,31,28,31,30,31,30,31,31,30,31,30,31};//初始化CDate::CDate(intm,intd,inty){ setDate(m,d,y);}voidCDate::setDate(intmm,intdd,intyy){month=(mm>=1&&mm<=12)?mm:1;year=(yy>=1900&&yy<=2100)?yy:1900;//判斷是否閏年if(month==2&&leapYear(year))day=(dd>=1&&dd<=29)?dd:1;elseday=(dd>=1&&dd<=days[month])?dd:1;}CDate&CDate::operator++(){helpIncrement();return*this;//返回當前對象}CDateCDate::operator++(int){CDatetemp=*this;helpIncrement();returntemp;//返回Date類型的對象}constCDate&CDate::operator+=(intadditionalDays)//增加指定天數到當前日期上{for(inti=0;i<additionalDays;i++) helpIncrement();return*this; }boolCDate::leapYear(inty)//判斷是否閏年{if(y%400==0||(y%100!=0&&y%4==0))returntrue;//是閏年elsereturnfalse;//不是閏年}boolCDate::endOfMonth(intd){if(month==2&&leapYear(year))returnd==29;//lastdayofFeb.inleapyearelsereturnd==days[month];}voidCDate::helpIncrement()//計算日期函數{if(endOfMonth(day)&&month==12)//年末 {day=1;month=1;++year; }elseif(endOfMonth(day))//月末 {day=1;++month; }else++day;}ostream&operator<<(ostream&output,constCDate&d){staticchar*monthName[13]={"","January","February","March","April","May","June","July","August","September","October","November","December"}; output<<monthName[d.month]<<''<<d.day<<","<<d.year;returnoutput;}voidmain(void){ CDated1,d2(12,27,1992),d3(0,99,8045); cout<<"d1is"<<d1 <<"\nd2is"<<d2 <<"\nd3is"<<d3<<"\n"; cout<<"d2+7is"<<(d2+=7)<<"\n"; d3.setDate(2,28,1992); cout<<"d3is"<<d3; cout<<"\n++d3is"<<++d3<<"\n"; CDated4(3,18,1969); cout<<"Testingthepreincrementoperator:\n" <<"d4is"<<d4<<'\n'; cout<<"++d4is"<<++d4<<'\n'; cout<<"d4is"<<d4<<"\n"; cout<<"Testingthepostincrementoperator:\n"<<"d4is"<<d4<<'\n'; cout<<"d4++is"<<d4++<<'\n'; cout<<"d4is"<<d4<<endl;}輸出結果:dlisJanuary1,1900↙d2isDecember27,1992↙d3isJanuary1,1900↙d2+=7isJanuary3,1993↙d3isFebruary28,1992↙++d3isFebruary29,1992↙Testingthepreincrementoperator:↙d4isMarch18,1969↙++d4isMarch19,1969↙d4isMarch19,1969↙Testingthepreincrementoperator:↙d4isMarch19,1969↙d4++isMarch19,1969↙d4isMarch20,1969↙
C++語言的運算符的操作數個數、優先級和結合性是其三個最基本的特性。在重載運算符時,應盡可能保持基原有的特性,而無須采取專門的措施。
重載運算符時需要注意下列問題:是否要求第一操作數就是左值操作數;是否修改其第一操作數;操作的結果是否為左值數據;保證第二操作數不被改變。
§11.2模板函數模板
所謂函數模板是一系列相關函數的模型,這些函數的源代碼形式相同,只是所針對的數據類型不同而已。
類模板類模板是一系列相關類的模型,這些類的成員組成相同,成員函數的源代碼形式相同,所不同的只是所針對的類型(包括數據成員的類型、成員函數的參數類型以及函數返回值的類型)。一、函數模板函數模板定義的一般形式如下:template〈typename<類型名稱1>,typename<類型名稱2>,……typename<類型名稱n>〉<數據類型>〈函數名稱〉(<形式參數說明列表>){函數實現語句序列;}其中,template關鍵字表示聲明的是模板,<
>內是模板的參數,可以有一個或n個,n≥1,斜體尖括弧是語句的成分;<類型名稱i>是一個標識符,其命名符合標識符命名規則,是虛擬的類型;函數返回值的<數據類型>可以是普通類型,也可以是模板形式類型參數表中指定的<類型名稱i>的數據類型;<形式參數說明列表>中給出了函數的若干個形式參數,他們可以是普通類型,也可以是模板形式類型參數表中指定的<類型名稱i>的數據類型;函數體中定義的對象參數,他們可以是普通類型,也可以是模板形式類型參數表中指定的<類型名稱i>的數據類型;typename可以用class代替,它們完全等價;也可以是一個實際的數據類型,而其后的<類型名稱i>就是該實際的數據類型的類型別名。模板函數的使用形式如下:〈函數名稱〉〈<數據類型1>,…<數據類型n>〉(<實參列表>)
或者
〈函數名稱〉(<實參列表>)
【例11.3】簡單的函數模板實例#include"iostream.h"#include"stdio.h" //函數模版定義template<typenameT>Tabs(Tx)//求絕對值{ Ty; if(x>0) y=x; else y=-x; return(y);}voidmain(void){ intm=-18; cout<<"m="<<m<<endl; cout<<"abs(m)="<<abs(m)<<endl; floatn=3.5; cout<<"n="<<n<<endl; cout<<"abs(n)="<<abs(n)<<endl; doublep=-12e10; cout<<"p="<<p<<endl; cout<<"abs(p)="<<abs(p)<<endl;cout<<"abs<double>(-1979)="<<abs<double>(1979)<<endl;}輸出結果:m=-18↙abs(m)=18↙n=3.5↙abs(n)=3.5↙p=-12e010↙abs(p)=12e010↙abs<double>(-1979)=1979↙在使用函數模板時需要注意:函數模板中的每個類型參數前都要放上class關鍵字,否則屬于語法錯誤。函數模板提供了軟件復用的好處。但是請記住:盡管函數模板只編寫一次,但程序中仍然實例化多個函數模板的副本。這些副本仍然會占用大量內存。二、類模板類模板定義的一般形式如下:template<typename<類型名稱1>,typename<類型名稱2>,……typename<類型名稱n>>class<類模板名>{成員說明語句序列;};template<typename<類型名稱1>,typename<類型名稱2>,……typename<類型名稱n>><數據類型><類模板名><模板形參表>::<函數名>(<函數形參表>){函數體中的語句序列;}其中,template是關鍵字,表示后面要定義的是類模板;在template后用〈〉括起的是模板的數據類型參數,參數可以有一個或多個,這些數據類型參數之間要用逗號隔開;typename<類型名稱i>中,關鍵字typename說明了定義的類是一個類模板,<類型名稱i>是類模板的類型參數。在類模板的定義中,<類型名稱i>可以為任意類型;類定義體中,數據成員類型或成員函數的參數、返回值以及函數體中的局部數據,他們可以是普通類型,也可以是模板參數表中指定的形式化的類型;<類型名稱i>是一個標識符,其命名規則符合標識符命名規則;typename可以用class代替,它們完全等價;也可以是一個實際的數據類型,而其后的<類型名稱i>就是該實際的數據類型的類型別名。<模板形參表>是〈<類型名稱1>,<類型名稱2>,...<類型名稱n>〉類模板實例化的一般形式如下:<類模板名>〈<類型名稱1>,<類型名稱2>,...<類型名稱n>〉
其中,通過類模板實現一個通用的特定類類型,需要一個或多個實際的數據類型參數,確定實際的數據類型形成特定的類類型,因此,類模板也常稱為參數化類類型。【例11.4】簡單的類模版實例#include"iostream.h"#include"stdio.h"template<typenameT,typenameP>//類模板的定義部分classCPlus{public: CPlus(); Pplus();public: Tm; Pn;};//類模板的實現部分template<typenameT,typenameP>CPlus<T,P>::CPlus(){}template<typenameT,typenameP>PCPlus<T,P>::plus(){ Ps; s=(P)(m+n); return(s);}voidmain(void)//用于測試類模板的程序{ CPlus<int,double>a; a.m=12; a.n=12.8766;cout<<"結果="<<a.plus()<<endl; CPlus<double,float>b; b.m=3.6543; b.n=5644.76f; cout<<"結果="<<b.plus()<<endl; CPlus<double,int>c; c.m=3.6543e+2; c.n=300; cout<<"結果="<<c.plus()<<endl;}輸出結果:結果=24.8766↙結果=5648.41↙結果=665↙從上例中我們看出,模板類與通常的類定義沒有什么不同,只是以如下所示的首部開頭:template<classT>它指出了這是一個類模板的定義,它有一類型參數T(表示所要建立的類的類型)。編程時不必專門使用標識符T,任何有意義的標識符都可以使用。在主程序定義對象的過程中(如CPlus<int,double>a語句),編譯系統會自動地根據需要生成相應的類定義,這種依據類模板生成類定義的過程稱為類模板的實例化。類模板所生成的每一個類定義就是相應類模板的一個實例類類型。在用類模板定義對象時,由于沒有像函數實參表那樣的額外信息渠道,因此無法按函數模板的方式省略模板類型實參。但是,可以為類模板的參數設置默認值。具體地說,在定義類模板時,可以為模板形參表聲明的最后若干個參數設置默認值;而這些有默認值的參數中,最后的若干個對應實參可以在定義對象時省略。【例11.5】堆棧類的模板實例。如果由一個數組構成了一個線性表,然后限定在表尾作插入、刪除操作就成為棧(也叫堆棧,用數組順序存儲空間表示的棧是一種順序棧)。//stack.h-堆棧類模板定義#include<iostream.h> template<classT>classStack{public:Stack(int=10); //默認構造函數,堆棧大小為10~Stack(){delete[]stackPtr;} boolpush(constT&); //入棧boolpop(T&); //出棧private:intsize; //堆棧大小inttop; //棧頂元素T*stackPtr; //堆棧指針boolisEmpty()const{returntop==-1;}//是否為空boolisFull()const{returntop==size-1;}//是否已滿};//stack.cpp-堆棧類模板實現#include"stack.h"template<classT>Stack<T>::Stack(intS){size=S>0?S:10;top=-1; //堆棧不可用stackPtr=newT[size]; //分配空間} template<classT>boolStack<T>::push(constT&pushValue){if(!isFull()){ stackPtr[++top]=pushValue; //入棧 returntrue; }returnfalse;}template<classT>boolStack<T>::pop(T&popValue){if(!isEmpty()){popValue=stackPtr[top--];
//出棧returntrue;
}returnfalse;
}//mainapp.cpp-測試堆棧模板類#include<iostream.h>#include"stack.h" intmain(){Stack<double>doubleStack(5);doublef=1.1;cout<<"PushingelementsontodoubleStack\n";while(doubleStack.push(f)){
cout<<f<<'';f+=1.1;} cout<<"\nStackisfull.cannotpush"<<f
<<"\n\nPoppingelementsfromdoubleStack\n"; while(doubleStack.pop(f))
cout<<f<<''; cout<<"\nStackisempty.Cannotpop\n"; Stack<int>intStack;inti
=1;cout<<"\nPushingelementsontointStack\n"; while(intStack.push(i)){
cout<<i<<'';
++i;} cout<<"\nStackisfull.Cannotpush"<<i
<<"\n\nPoppingelementsfromintStack\n"; while(intStack.pop(i))
cout<<i<<''; cout<<"\nStackisempty.Cannotpop\n";return0;}輸出結果:PushingelementsontodoubleStack↙1.12.23.34.45.5↙Stackisfull.Caunotpush6.6↙ PoppingelementsfromdoubleStack↙5.54.43.32.
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年教育心理學教師資格考試試卷及答案
- 2025年技能型人才培養與發展考試卷及答案
- 2025年公共關系職業素養測驗試卷及答案
- 稻谷種植政策與補貼解讀考核試卷
- 鐵路車輛維修流程優化與效率提升考核試卷
- 混行下CAV多信號交叉口綠燈優化車速引導策略設計與驗證
- PS-PHMS多尺度納米纖維膜的制備及空氣過濾應用
- 起重設備質量控制與品質保證考核試卷
- 納米鋁錐陣列等離激元晶格調控與研究
- SETDB2結合HNRNPC抑制PI3K-AKT通路活化和肺癌放療抵抗
- 體育測量與評價PPT課件-第九章 運動員選材的測量與評價
- 中國心力衰竭患者離子管理專家共識
- 四川省綿陽市2021年中考生物考試真題與答案解析
- 北師大版六年級下冊數學期末試題(6套)
- 《園林植物識別與應用》項目七:綜合課業題庫及答案
- 人民醫院腫瘤科臨床技術操作規范2023版
- 生態學課件 第五章 生態系統生態學
- 路燈桿強度計算簡述
- GB/T 912-2008碳素結構鋼和低合金結構鋼熱軋薄鋼板和鋼帶
- GB/T 4851-2014膠粘帶持粘性的試驗方法
- GB/T 18838.2-2017涂覆涂料前鋼材表面處理噴射清理用金屬磨料的技術要求第2部分:冷硬鑄鐵砂
評論
0/150
提交評論