




版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
改善程序設(shè)計(jì)技術(shù)的
50個(gè)有效做法
如何完成較好的設(shè)計(jì)如何避免常見的問題如何提高效率的一些準(zhǔn)則不是放之四海而皆準(zhǔn)的唯一真理C++新標(biāo)準(zhǔn)新的類型bool有兩個(gè)值true,false.typedefintbool;constboolfalse=0;constbooltrue=1;1.盡量以const和inline取代#define#define
是一個(gè)宏,只能被預(yù)處理,而不被編譯,用它定義的常量甚至不被編譯器看見,因此不能發(fā)現(xiàn)使用中的錯(cuò)誤。用#define定義一個(gè)簡(jiǎn)單函數(shù),必須為每一個(gè)參數(shù)加上一個(gè)括號(hào),容易造成錯(cuò)誤。用內(nèi)聯(lián)函數(shù)高效準(zhǔn)確。#defineratio1.653
//編譯器看不見ratio,只看見1.653
//一旦出錯(cuò),不會(huì)報(bào)告constdoubleratio=1.653;constchar*constname=“ScottMeyers”;//字符串常量InClass常量用靜態(tài)變量類內(nèi)聲明,類外定義。classEngineerConstants{
private:
staticconstdoubleFactor;……};constdoubleEngineerConstants::Factor=1.35;2.盡量以<iostream>取代<stdio.h>scanfprintf函數(shù)不能擴(kuò)充用來(lái)輸入輸出自定義類型的變量。cin>>i>>x;cout<<i<<x;可以擴(kuò)展,方便得多改變舊有的C習(xí)慣(shiftingfromCtoC++)盡量以const和inline取代#define#define是一個(gè)宏,只能被預(yù)處理,而不被編譯,用它定義的常量甚至不被編譯器看見,因此不能發(fā)現(xiàn)使用中的錯(cuò)誤。用#define定義一個(gè)簡(jiǎn)單函數(shù),必須為每一個(gè)參數(shù)加上一個(gè)括號(hào),容易造成錯(cuò)誤。用內(nèi)聯(lián)函數(shù)高效準(zhǔn)確。3.盡量以new和delete取代malloc和freemalloc和free不能調(diào)用構(gòu)造函數(shù),析構(gòu)函數(shù)
new和delete則可。不能混用newdeletemallocfree必要用C庫(kù)函數(shù)時(shí)檢查是否用到malloc重新用new和delete改過(guò)。4.盡量使用C++風(fēng)格的注釋形式/*……*/要保證成對(duì)出現(xiàn),不小心錯(cuò)一大片。
//好看好讀可以混合使用當(dāng)心?。efinelight_speed3e8//m/sec(inavacum)內(nèi)存管理(memorymanagement)
new隱式調(diào)用構(gòu)造函數(shù),delete隱式調(diào)用析構(gòu)函數(shù),
可以重載operatornew和operatordelete.不小心運(yùn)用new和delete會(huì)導(dǎo)致各種錯(cuò)誤。5.使用相同形式的new和deletestring*a=newstring[10];……deletea;//出錯(cuò)delete[]a;//正確string*b=newstring;……delete[]b;//出錯(cuò)deleteb;//正確typedefstringaddresslines[4];string*a=newaddresslines;……deletea;//出錯(cuò)delete[]a;//正確不要對(duì)數(shù)組類型用typedef,不容易記住用哪一種delete6.記得在析構(gòu)函數(shù)中以delete對(duì)付指針成員如果類中有指針數(shù)據(jù)成員,在每個(gè)構(gòu)造函數(shù)中為指針成員配置內(nèi)存,否則將它初始化為0(NULL指針)。若構(gòu)造函數(shù)中用new配置了內(nèi)存,一定要在析構(gòu)函數(shù)中用delete釋放在賦值運(yùn)算重載時(shí)要將原有指針的內(nèi)存刪除,重新分配內(nèi)存。要在析構(gòu)函數(shù)中刪除這個(gè)指針。不要用delete刪除一個(gè)未完成初始化的指針,不要?jiǎng)h除一個(gè)未分配內(nèi)存的指針。不要delete從一個(gè)類外面?zhèn)鱽?lái)的指針。7.為內(nèi)存不足的狀況預(yù)作準(zhǔn)備
不能認(rèn)為“檢查內(nèi)存是否分配成功”是多此一舉。否則會(huì)出現(xiàn)嚴(yán)重后果。必須建立一個(gè)錯(cuò)誤處理策略。當(dāng)operatornew無(wú)法滿足需求時(shí),在拋出異常之前,會(huì)調(diào)用一個(gè)內(nèi)存不足處理函數(shù)newhandler,這個(gè)函數(shù)由頭文件<new>提供。
typedefvoid(*new_handle)();
new_handlerset_new_handler(new_handlerp)throw();
new_handler是一個(gè)函數(shù)指針,無(wú)參,無(wú)返回值。函數(shù)set_new_handler用來(lái)配置new_handler,參數(shù)和返回值都是函數(shù)指針,new_handler類型。它確定新的new_handler函數(shù)(參數(shù)),保留舊的new_handler函數(shù)(返回)。可以自定義新的new_handler函數(shù),
用set_new_handler確認(rèn)。voidnomoreMemory(){cerr<<“Unabletosatisfyformemeory\n”abort();//exit}intmain(){set_new_handler(nomoreMemory);int*pBigDataArray=newint[100000000];……}當(dāng)operatornew無(wú)法配置10000000個(gè)整數(shù)空間時(shí),系統(tǒng)調(diào)用nomoreMemory,然后結(jié)束。設(shè)計(jì)new_handler函數(shù),令其完成如下任務(wù):--讓更多的內(nèi)存可用。預(yù)留一塊內(nèi)存,
new_handler第一次被調(diào)用時(shí)釋放,同時(shí)發(fā)出警告。安裝新的new_handler以取代自己。new_handler中調(diào)用C++標(biāo)準(zhǔn)庫(kù)函數(shù)set_new_handler即可。卸載這個(gè)new_handler,返回NULL指針,并拋出bad_alloc(或其繼承)類型的異常。直接調(diào)用abort或exit終止程序。C++不支持class中專用的new_handler,但仍可以在一個(gè)class中重載operatornew,和set_new_handler函數(shù),讓它調(diào)用特定的new_handler函數(shù),而不用系統(tǒng)給出的全局new_handler。classX{public:
staticnew_handlerset_new_handler(new_handlerp);staticvoid*operatornew(size_tsiz);private:staticnew_handlercurrentHandler;};new_handlerX::currentHandler;//初始化為0new_handlerX::set_new_handler(new_handlerp){new_handleroldHandler=currentHandler;
//保留當(dāng)前new_handlercurrentHandler=p;//再設(shè)置當(dāng)前new_handlerreturnoldHandler;}void*X::operatornew(size_tsize){newHandlerglobalHandler=std::set_new_handler(currentHandler);
//配置新new_handler保存globalHandlervoid*memory;try{memory=::opratornew(size);//試分配內(nèi)存
}catch(std::bad_alloc&){std::set_new_handler(globalHandler);
//恢復(fù)原有處理方法
throw;//傳播異常
}std::set_new_handler(globalHandler):
//恢復(fù)原有處理方法
returnmemory;
}//調(diào)用一次特定處理方法,用畢恢復(fù)//應(yīng)用voidnomoreMemory();X::set_new_handler(nomoreMemory);X*px1=newX;
//如果內(nèi)存分配失敗,調(diào)用nomoreMemory()string*ps=newstring;
//如果內(nèi)存分配失敗,調(diào)用globalHandlerX::set_new_handler(0);X*px2=newX;
//如果內(nèi)存分配失敗,立即拋出異??梢宰鲆粋€(gè)混合風(fēng)格基類
允許“設(shè)定class專屬new_handler”template<classT>classNewHandlerSupport{public:
staticnew_handlerset_new_handler(new_handlerp);staticvoid*operatornew(size_tsiz);private:staticnew_handlercurrentHandler;};template<classT>new_handlerNewHandlerSupport<T>::set_new_handler(new_handlerp){new_handleroldHandler=currentHandler;//保留當(dāng)前new_handlercurrentHandler=p;//再設(shè)置當(dāng)前new_handlerreturnoldHandler;}template<classT>void*NewHandlerSupport<T>::operatornew(size_tsize)
{newHandlerglobalHandler=std::set_new_handler(currentHandler);
//配置新new_handler保存globalHandlervoid*memory;try{memory=::opratornew(size);//試分配內(nèi)存}catch(std::bad_alloc&){std::set_new_handler(globalHandler);
//恢復(fù)原有處理方法
throw;//傳播異常
}std::set_new_handler(globalHandler):
//恢復(fù)原有處理方法
returnmemory;}new_handlerNewHandlerSupport<T>
::currentHandler;//初始化為0classX:publicNewHandlerSupport<X>{……
//不必聲明set_new_handler和operatornew}類X不必改動(dòng)原有的程序代碼,就可以繼續(xù)運(yùn)作。1993年前C++要求operatornew在無(wú)法滿足內(nèi)存需求時(shí)返回0,新標(biāo)準(zhǔn)則是拋出一個(gè)bad_alloc類型異常。失敗便轉(zhuǎn)為0的傳統(tǒng)被保留為“nothrow”不拋出異常。<new>頭文件中定義了一個(gè)nothrow對(duì)象
classWidget{……};Widget*pw1=newWidget;//如果失敗拋出std::bad_alloc異常
if(pw1==0)……//無(wú)效
widget*wp2=new(nothrow)Widget;
//如果失敗,返回0if(wp2==0)……//有效8.撰寫operatornew和operatordelete時(shí)
應(yīng)遵守的公約
當(dāng)你有必要重載operatornew時(shí),你的new函數(shù)的行為應(yīng)該與系統(tǒng)原有的new函數(shù)的行為保持一致。應(yīng)該有正確的返回值:返回一個(gè)指針指向分配的內(nèi)存。如果內(nèi)存不足,拋出一個(gè)bad_alloc類型的異常。不能覆蓋系統(tǒng)原有的new函數(shù)。//new
函數(shù)的偽碼void*operatornew(size_tsize){if(size==0){size=1;}
//將0內(nèi)存需求,看成1內(nèi)存需求,避免與無(wú)內(nèi)存混淆
while(true)//無(wú)窮循環(huán)直到內(nèi)存被分配或拋出異常
{attempttoallocatesizebytes;if(theallocationwassuccessful)return(apointertothememory);new_handleglobalHandle=set_new_handler(0)
//利用NULL,找出目前的錯(cuò)誤處理函數(shù)
set_new_handler(globalHandler);
//重新設(shè)定為原本的函數(shù)
if(globalHandler)(*globalHandler)()elsethrowstd::bad_alloc();}}無(wú)窮循環(huán)可以讓更多的內(nèi)存可用,
或安裝一個(gè)不同的new_handler,
或卸載new_handler,
或拋出一個(gè)異常,
或直接結(jié)束程序。
operatornew可以被繼承,
但要小心,否則會(huì)導(dǎo)致問題classBase{public:staticvoid*opratornew(size_tsize);……};classDerived:publicBase{……};//導(dǎo)出類中沒有operatornew函數(shù)Derived*p=newDerived;
//調(diào)用Base類中的operatornew出錯(cuò)這里導(dǎo)出類內(nèi)存比基類要大。改進(jìn)的辦法:void*operatornew(size_tsize){……if(size!=sizeof(Base)return::opratornew(size);
//回到標(biāo)準(zhǔn)operatornew函數(shù)
……}重寫operatordeletevoidoperatordelete(void*rawMemory){if(rawMemory==0)return;//與C++標(biāo)準(zhǔn)delete保持一致
DeallocatethememorypointedtobyrawMemory;return;}//member版classBase{public:staticvoid*operatornew(size_tsize);
staticvoidoperatordelete(void*rawMemory,size_tsize);……}voidBase::operatordelete(void*rawMemory,size_tsize);{if(rawMemory==0)return;if(size!=sizeof(Base)//如果大小錯(cuò)誤
{::operatordelete(rawMemory);
//用標(biāo)準(zhǔn)版delete處理
return;}deallocatethememorypointedtobyrawmeMemory;return;}9.避免覆蓋new的正規(guī)形式解決辦法
(1)再寫的一個(gè)專用的operatornew函數(shù),讓它支持正規(guī)的newclassX{public:voidf();staticvoid*operatornew(size_tsize,new_handlerp);staticvoid*operatornew(sise_tsize){return::operatornew(size);}};X*p1=new(specialErrorHandler)X;//調(diào)用X::operatornew(size_tsize,new_handlerp);X*p2=newX;//調(diào)用X::operatornew(size_tsize);
(2)為operatornew的每一個(gè)參數(shù)提供默認(rèn)值
(缺省值)10.如果寫了一個(gè)operatornew
不要忘記寫一個(gè)operatordelete需要?jiǎng)討B(tài)分配大量小額內(nèi)存空間的應(yīng)用程序,有時(shí)需要重載operatornew。classAirplaneRep{……};classAirplane{public:……private:AirplaneRep*rep;//唯一數(shù)據(jù)成員是指針};Airplane*p=newAirplane;//要求內(nèi)存不大分配的內(nèi)存比實(shí)際所需要的內(nèi)存要大,這是為了delete這塊內(nèi)存時(shí),系統(tǒng)能知道其大小。pa
紀(jì)錄內(nèi)存大小的數(shù)據(jù)Airplane對(duì)象所需的內(nèi)存為了節(jié)省內(nèi)存需要定制內(nèi)存管理。定制內(nèi)存管理。classAirplane{public:staticvoid*operatornew(size_tsize);
staticvoidoperatordelete(void*deadObject,size_tsize);……private:union{AirplaneRep*rep;Airplane*next;};//兩個(gè)指針公用一個(gè)內(nèi)存
staticconstintBLOCK_SIZE;staticAirplane*headOfFreeList;
//用鏈表配置一片內(nèi)存,整個(gè)類只須一個(gè)鏈};void*Airplane::operatornew(size_tsize);{if(size!=sizeof(Airplane))return::operatornew(size);Airplane*p=headOfFreeList;
//p指向鏈表頭
if(p)headOfFreeList=p->next;//表頭后移,p可用
else{Airplane*newBlock=static_cast<Airplane*>(::operatornew(BLOCK_SIZE*sizeof(Airplane)));for(inti=1;i<BLOCKSIZE-1;++i)//保留第一塊
newBlock[i].next=&newBlock[i+1];newBlock[BLOCK_SIZE-1].next=0;//置表尾
p=newBlok;//p可用
headOfFreeList=&newBlock[1];}returnp;}只有當(dāng)::operatornew失敗時(shí),這里的operatornew才失敗。這時(shí)::operatornew會(huì)調(diào)用new_handler直到拋出異常,因此我們不需要再寫一次new_handler處理具體實(shí)現(xiàn)文件中要先對(duì)靜態(tài)成員初始化,Airplane*Airplane::headOfFreeList;
//headOfFreeList置0constintAirplane::BLOCK_SIZE=512;這個(gè)版本的operatornew可以運(yùn)作良好,速度快過(guò)兩個(gè)數(shù)量級(jí)。還要在Airplane類中寫一個(gè)operatordeletevoidAirplane::operatordelete(void*deadObject,size_tsize){if(deadObject==0)return;if(size!=sizeof(Airplane)){::operatordelete(deadObject);
//與operatornew處理保持一致
return;}Airplane*carcass=static_cast<Airplane>(deadObject);carcass->next=headOfFreeList;HeadOfFreeList=carcass;}如果沒有定義相應(yīng)的delete函數(shù),而使用了原有的delete,結(jié)果會(huì)出現(xiàn)意想不到的錯(cuò)誤,有時(shí)是嚴(yán)重的錯(cuò)誤。如果用member版本不要忘記定義virtual析構(gòu)函數(shù)。這里的delete函數(shù)沒有memoryleak問題。
這是因?yàn)橛昧薽emorypool一次分配一塊內(nèi)存,逐步使用逐步釋放,不必再專門釋放memorypool.定義一個(gè)memorypool類,使每一個(gè)pool對(duì)象都是一個(gè)內(nèi)存配置器。classPool{public:Pool(size_tn);void*alloc(size_tn);//為一個(gè)對(duì)象配置足夠
//的內(nèi)存遵循operatornew的規(guī)矩
voidfree(void*p,size_tn);//將p的內(nèi)存送回
//pool遵循operatordelete的規(guī)矩
~pool();//釋放pool中所有內(nèi)存};用Pool對(duì)象來(lái)配置內(nèi)存,當(dāng)被銷毀時(shí),配置的內(nèi)存自動(dòng)被釋放。于是memoryleak就可以避免。classAirplane{public:staticvoid*operatornew(size_tsize);staticvoidoperatordelete(void*p,size_tsize);……private:AirplaneRep*rep;staticPoolmemPool;//Airplane的memorypool};inlinevoidAirline::operatornew(size_tsize){returnmemPool.alloc(size);}inlinevoidAirline::operatordelete(void*p,size_tsize){memPool.free(p,size);}為Airplane的memPool初始化,要放在Airplane類實(shí)現(xiàn)的文件里PoolAirplane::memPool(sizeof(Airplane));構(gòu)造函數(shù)、析構(gòu)函數(shù)和賦值運(yùn)算符
構(gòu)造函數(shù)、析構(gòu)函數(shù)和賦值運(yùn)算用來(lái)產(chǎn)生一個(gè)新對(duì)象并初始化,撤銷一個(gè)對(duì)象并收回占有的內(nèi)存,為已有的對(duì)象賦一個(gè)新值。不能有錯(cuò),必須將他們徹底搞清楚。
11.class內(nèi)有成員指針并動(dòng)態(tài)配置內(nèi)存時(shí),一定要有拷貝構(gòu)造函數(shù),賦值運(yùn)算符重載classString{public:String(constchar*value);~String();……//沒有拷貝構(gòu)造函數(shù),
//也沒有賦值運(yùn)算符重載
private:char*data;};String::String(constchar*value){if(value){data=newchar[strlen(value)+1];strcopy(data,value);}else{data=newchar[1];*data=“\0”;}}inlineString::~String(){delete[]data;}Stringa(“Hello”);Stringb(“World”);b=a;HelloWorldabdatadata由于沒有自定義的賦值函數(shù),只能用C++產(chǎn)生的默認(rèn)賦值函數(shù),它簡(jiǎn)單地將b的成員指針data指向a.data,引起字符串“World”占有的內(nèi)存遺失。而且a.data與b.data指向同一個(gè)內(nèi)存,其中一個(gè)被析構(gòu)時(shí)另一個(gè)就丟失了??截悩?gòu)造函數(shù)用來(lái)傳值,voiddonothing(Stringla){}Strings=“thetruthisoutofthere”;donothing(s);當(dāng)函數(shù)donothing完成任務(wù)后,參數(shù)s所含的指針被析構(gòu),la被刪除。即便la不再使用,將來(lái)又一次析構(gòu)la會(huì)造成問題。解決的辦法就是自己定義拷貝構(gòu)造函數(shù),賦值函數(shù)重載。如果確信永不使用這些函數(shù),把他們定義為私有函數(shù),而且不實(shí)現(xiàn)。一旦出錯(cuò),編譯器會(huì)給出錯(cuò)誤提示。12構(gòu)造函數(shù)中盡量以初始化代替賦值
一個(gè)類中的const成員數(shù)據(jù)和reference引用數(shù)據(jù)只能被初始化,不能被賦值。即便沒有const成員數(shù)據(jù)和reference引用數(shù)據(jù),初始化也比賦值效率高。構(gòu)造函數(shù)分兩個(gè)階段實(shí)現(xiàn):
1.數(shù)據(jù)成員初始化。
2.調(diào)用構(gòu)造函數(shù)。數(shù)據(jù)成員賦值要調(diào)用構(gòu)造函數(shù),再調(diào)用賦值函數(shù),做兩次調(diào)用影響效率。初始化也容易維護(hù),修改。有一種例外:一個(gè)類內(nèi)有大量數(shù)據(jù)成員時(shí),賦值比初始化效率高。classManyDataMbs{public:ManyDataMbs()
ManyDataMbs(constManyDataMbs&x);private:inta,b,c,d,e,f,g,h;doublei,j,k,l,m;voidinit();//用來(lái)將數(shù)據(jù)成員初始化,不做他用};voidManyDataMbs::init(){a=b=c=d=e=f=g=h=1;i=j=k=l=m=0;}ManyDataMbs::ManyDataMbs(){init();……}ManyDataMbs::ManyDataMbs(constManyDataMbs&x){init();……}靜態(tài)數(shù)據(jù)成員staticclassmember不應(yīng)該在構(gòu)造函數(shù)中初始化。靜態(tài)數(shù)據(jù)成員只能初始化一次,不能初始化多次。12.數(shù)據(jù)成員初始化的次序應(yīng)該和類內(nèi)聲明的次序相同template<classT>classArray//有上下界的數(shù)組{public:Array(intlowBound,inthighBound);……private:vector<T>data;//數(shù)組數(shù)據(jù)存儲(chǔ)于一個(gè)vector對(duì)象data中
size_tsize;//數(shù)組中元素的個(gè)數(shù)
intlBound,hBound;//上下界};template<classT>Array<T>::Array(intlowBound,inthighBound):size(highBound-lowBound+1),lBound(lowBound),hBound(highBound),data(size){}實(shí)際初始化中,data先被初始化,然后size,lBound,hBound.這樣數(shù)組中,究竟有多少個(gè)元素?zé)o法確定?;惓蓡T總是比導(dǎo)出類先初始化。多重繼承時(shí)初始化的先后次序要十分小心。14.總是讓基類擁有虛析構(gòu)函數(shù)一個(gè)軍事應(yīng)用軟件classEnemyTarget{public:EnemyTarget(){++numTargets;}EnemyTarget(constEnemyTarget&){++numTargets;}~EnemyTarget(){--numTargets;}staticsize_tnumberOfTargets(){returnnumTargets;}virtualbooldestroy();//摧毀敵方目標(biāo)是否成功
private:staticsize_tnumTargets;//對(duì)象計(jì)數(shù)器};size_tEnemyTarget::numTargets;//靜態(tài)成員初始化為0,放在類外classEnemyTank:publicEnemyTarget{public:EnemyTank(){++numTanks;}EnemyTank(constEnemyTank&){++numTanks;}~EnemyTank(){--numTanks;}staticsize_tnumberOfTanks(){returnnumTanks;}virtualbooldestroy();//摧毀敵方坦克是否成功
private:staticsize_tnumTanks;//敵方坦克計(jì)數(shù)器};EnemyTarget*targetPtr=newEnemyTank;……deletetargetPtr;//未定義,計(jì)數(shù)出錯(cuò),影響戰(zhàn)斗勝敗解決辦法,把EnemyTarget類中的析構(gòu)函數(shù)定義為virtual即可。幾乎所有的基類都有虛函數(shù),只要有一個(gè)虛函數(shù),就要把析構(gòu)函數(shù)定義為虛函數(shù)。沒有虛函數(shù)的類,有繼承派生類對(duì)象析構(gòu),也要定義虛析構(gòu)函數(shù)。但虛函數(shù)會(huì)增加內(nèi)存開銷。完全不必要時(shí)不要用虛析構(gòu)函數(shù)。聲明一個(gè)抽象類,可以加一個(gè)純虛析構(gòu)函數(shù)。15.讓operator=返回*this的引用referenceC語(yǔ)言中operator=的原型C&C::operator=(constC&);charx,y,z;x=y=z=‘a(chǎn)’;x,operator=(y.operator=(z.operator=‘a(chǎn)’));z.operator=的返回值是y.operator=的實(shí)參。他們應(yīng)該有相同的類型。但不要讓operator=返回void類型,const類型Strin&String::operator=(constString&rhs){……return*this;//返回一個(gè)引用指向左側(cè)對(duì)象}Strin&String::operator=(constString&rhs){……returnrhs;//返回一個(gè)引用指向右側(cè)對(duì)象,錯(cuò)誤}后一個(gè)返回值,編譯器無(wú)法編譯,無(wú)法返回const類型.如果參數(shù)中去掉const變成:Strin&String::operator=(String&rhs);X=‘a(chǎn)’;//無(wú)法編譯rhs應(yīng)該是一個(gè)變量。結(jié)論:必須返回*this;16.在operator=中為所有的數(shù)據(jù)成員賦值
基類中這不成問題,在派生類中要小心。正確的賦值運(yùn)算Derived&Derived::operator=(constDrived&rhs){if(this==&rhs)return*this;Base::operator=(rhs);//調(diào)用基類的賦值運(yùn)算
data=rhs.data;return*this;}Derived&Derived::operator=(constDrived&rhs){if(this==&rhs)return*this;static_cast<Base&>(*this)=rhs;//*this強(qiáng)制轉(zhuǎn)換成基類的引用賦值基類成員
data=rhs.data;return*this;}
拷貝構(gòu)造函數(shù)中要調(diào)用基類構(gòu)造函數(shù)。用第一種方法在operator=中檢查是否“自己賦值給自己”
classX{……};Xa;X&b=a;//b是a的別名(aliasing)a=b;//自己賦值給自己合法
在賦值函數(shù)中要特別謹(jǐn)慎的處理自己的別名賦值給自己的問題。提高效率
先做檢查,一發(fā)現(xiàn)自己賦值給自己立即返回。導(dǎo)出類的賦值運(yùn)算重載中一定要先檢查,可以節(jié)省許多工作確保正確性
賦值運(yùn)算通常要先將左邊對(duì)象的資源釋放,再行賦值。如果有自己賦值給自己的現(xiàn)象,這個(gè)資源可能丟失,不可挽回了。如何判斷兩個(gè)對(duì)象是同一個(gè)對(duì)象?不是對(duì)象的內(nèi)容相同,而是看他們的地址是否相同。
X&X::operator=(constX&rhs){if(this==&rhs)return*this;……}
aliasing問題不限于賦值運(yùn)算內(nèi),只要用到指針或引用,就可能出現(xiàn)。這時(shí)我們就要當(dāng)心,不要誤刪了有用的資源。類和函數(shù)的設(shè)計(jì)和申明設(shè)計(jì)一個(gè)高效率的類型(class型別),必須先回答下列問題對(duì)象如何產(chǎn)生和銷毀?確定構(gòu)造函數(shù)和析構(gòu)函數(shù)的設(shè)計(jì)。對(duì)象的初始化和賦值有什么不同?決定構(gòu)造函數(shù)和賦值函數(shù)的設(shè)計(jì)。對(duì)象如何傳值決定拷貝構(gòu)造函數(shù)的設(shè)計(jì)確定合法的范圍成員數(shù)據(jù)的定義域確定做什么檢查,何時(shí)拋出異常判斷是否能從已有的類繼承如果能繼承,注意受基類哪些約束,哪些要用虛函數(shù)。允許那種類型轉(zhuǎn)換構(gòu)造函數(shù)可以用作隱式類型轉(zhuǎn)換,顯式類型轉(zhuǎn)換要自定義。新類型需要哪些運(yùn)算和函數(shù)確定class的接口。哪些運(yùn)算和函數(shù)必須禁用放到private成員中。新類型的對(duì)象可調(diào)用哪些函數(shù)確定公有成員函數(shù),保護(hù)成員函數(shù),私有成員函數(shù)。是否通用類型確定是否要用類模板18.努力讓接口完滿(complete)且最小化客戶端接口(clientinterface)指公有成員,一般只有公有函數(shù),不要有公有數(shù)據(jù)。完滿接口允許客戶做合理要求的任意事情。最小化接口盡量讓函數(shù)個(gè)數(shù)最少。不能有功能重疊的函數(shù)。太多函數(shù)不容易被理解,不易維護(hù),浪費(fèi)資源。如果增加一個(gè)函數(shù),使新類型更方便使用,就可以增加。T&operator[](intindex);//傳回?cái)?shù)組的一個(gè)元素,可讀,可寫constT&operator[](intindex)const;//傳回?cái)?shù)組的一個(gè)元素,可讀,不可寫19.區(qū)分成員函數(shù)、非成員函數(shù)
和友元函數(shù)
成員函數(shù)可以動(dòng)態(tài)綁定,可以用virtual非成員函數(shù)不能用virtual,非成員函數(shù)能不做友元盡量不做友元函數(shù)。非成員函數(shù)要調(diào)用類中私有數(shù)據(jù)成員或私有函數(shù),則一定要聲明為友元。不要讓operaor<<和operator>>成為類的成員函數(shù),必要時(shí)作友元。要讓函數(shù)式左邊對(duì)象做類型轉(zhuǎn)換,就不能做成員函數(shù)。例子classcomplex{……complexoperator*(complexrhs)const;private:floatx,y;};complexa(1,2),b(1.5,4);a=a*b;//正確a=a*2;//可以a=2*a;//出錯(cuò)只能聲明為非成員函數(shù)constcomplexoperator*(constcomplex&lhs,constcomplex&rhs);20.避免將數(shù)據(jù)成員設(shè)置為公有數(shù)據(jù)
讓公有成員都是函數(shù),可以保持一致性。將數(shù)據(jù)成員聲明為私有成員或保護(hù)成員,可以確保數(shù)據(jù)的安全。21.盡可能使用const
使用const可以讓編譯器知道某值不能改變,編譯器會(huì)確保這個(gè)條件不會(huì)被改變。constchar*p;//指針,指向常值字符串char*constp;//常指針,指向固定地址,地址內(nèi)字符串不一定是常量constchar*constp;//常指針,指向固定地址,內(nèi)置常字符串constchr*p;charconst*p;//意義相同函數(shù)中const可以修飾傳回值,參數(shù),成員函數(shù)時(shí)甚至可以修飾整個(gè)函數(shù)。函數(shù)返回值用const,可以改善函數(shù)的安全性,和效率。T&A<T>::operator[](intindex);//傳回?cái)?shù)組的一個(gè)元素,可讀,可寫Aa(8);cout<<a[2];a[2]=’b’;//正確constT&A<T>::operator[](intindex);//傳回?cái)?shù)組的一個(gè)元素,可讀,不可寫
Aa(8);cout<<a[2];//正確
a[2]=’b’;//錯(cuò)誤constcomplexoperator*(constcomplex&lhs,constcomplex&rhs);complexa,b,c;(a*b)=c;//不允許參數(shù)用const可以保證參數(shù)值不變,讓編譯器作檢查。const成員函數(shù)保證this指針不變。classA{public:……intlength()const;private:intsize;};intA::length()const{if(size<0)return0;//錯(cuò)誤不能改變?nèi)魏螖?shù)據(jù)成員
returnsize;}新C++標(biāo)準(zhǔn)新增保留字mutableclassA{public:……intlength()const;private:
mutableintsize;//可以在任何地點(diǎn)被改動(dòng),
//即使在const成員函數(shù)中};intA::length()const{if(size<0)return0;//正確
returnsize;}22.盡量使用引用參數(shù)傳址
passbyreference
拷貝構(gòu)造函數(shù)用來(lái)傳值passbyvalue,為函數(shù)的參數(shù)傳值,為函數(shù)的返回值傳值。傳值要占用許多資源。classPerson{public:Person();~Person();……private:stringname,address;};
classstudent:publicPerson{public:student();~student();private:stringschoolname,schoolAddress;};
studentreturnstudent(students){returns;}
studentplato;returnstudent(plato);
函數(shù)調(diào)用中copy構(gòu)造函數(shù)被調(diào)用兩次,將plato傳給參數(shù)s,再將函數(shù)值返回,析構(gòu)函數(shù)調(diào)用兩次,析構(gòu)s,析構(gòu)函數(shù)返回值。更有甚者,基類Person的構(gòu)造函數(shù)也要調(diào)用兩次。student對(duì)象中兩個(gè)string數(shù)據(jù)對(duì)象要構(gòu)造,基類Person中兩個(gè)string數(shù)據(jù)對(duì)象也要構(gòu)造,plato給s構(gòu)造四次,返回傳值構(gòu)造四次總共調(diào)用12次構(gòu)造函數(shù),當(dāng)然還有12次析構(gòu)函數(shù)要調(diào)用。免除這些不當(dāng)成本,改用引用參數(shù)傳址byreference
conststudent&returnstudent(conststudent&s){returns;}引用參數(shù)傳址byreference不調(diào)用任何構(gòu)造函數(shù)析構(gòu)函數(shù)。虛函數(shù)的引用參數(shù)是基類時(shí),實(shí)際傳入派生類對(duì)象時(shí)可以調(diào)用派生類的函數(shù)。傳值參數(shù)沒有這樣的功能。
引用參數(shù)要注意別名(aliasing)問題。23.當(dāng)你必須傳回objebct(傳值)時(shí)不要傳址(引用)
盡可能讓事情簡(jiǎn)單,但不要過(guò)于簡(jiǎn)單。
A.Einstein盡可能讓事情有效率,但不要過(guò)于有效率。
C++函數(shù)必須傳回一個(gè)對(duì)象,就不要傳址不要返回引用。不能傳回一個(gè)不存在的地址,不能傳回函數(shù)中產(chǎn)生的局部對(duì)象的地址。constcomplex
operator*(constcomplex&lhs,constcomplex&rhs){complextemp(lhs.x*rhs.x-lhs.y*rhs.y,lhs.x*rhs.y+lhs.y*rhs.x);returntemp;}
&錯(cuò)誤。返回值地址指向局部對(duì)象,與局部對(duì)象同名,運(yùn)算執(zhí)行完畢,局部對(duì)象被析構(gòu),返回值指向一個(gè)不存在的地址。constcomplex&operator*(constcomplex&lhs,constcomplex&rhs){complex*temp(lhs.x*rhs.x-lhs.y*rhs.y,lhs.x*rhs.y+lhs.y*rhs.x);return*temp;}指針temp被析構(gòu),內(nèi)存已丟失。。constcomplex&operator*(constcomplex&lhs,constcomplex&rhs){complex*temp=newcomplex(lhs.x*rhs.x-lhs.y*rhs.y,lhs.x*rhs.y+lhs.y*rhs.x);return*temp;}指針temp沒有析構(gòu),將來(lái)誰(shuí)來(lái)析構(gòu)呢。內(nèi)存可能丟失。complexone(1),two(2),three(3),four(4);complexproduct;product=one*two*three*four;如何析構(gòu)這幾個(gè)operator*中間產(chǎn)生的temp指針呢?24.函數(shù)重載和參數(shù)缺省之間,謹(jǐn)慎抉擇函數(shù)重載和參數(shù)缺省之間容易引起混淆。如果可以選擇一個(gè)合理的默認(rèn)值,并且只需要一種算法,最好使用缺省參數(shù)。否則使用重載函數(shù)。例:求五個(gè)整數(shù)的最大值#include<limits>intmax(inta,intb=std::numeric_limits<int>::min(),intc=std::numeric_limits<int>::min(),intd=std::numeric_limits<int>::min(),inte=std::numeric_limits<int>::min(),){inttemp=a>b?a:b;inttemp=temp>c?temp:c;inttemp=temp>d?temp:dinttemp=temp>e?temp:e;}使用max函數(shù)對(duì)兩個(gè)參數(shù),三個(gè)參數(shù),直至五個(gè)參數(shù)都有效。但是,計(jì)算平均數(shù)就找不到合適的默認(rèn)值,只好重載。一般,構(gòu)造函數(shù)和拷貝構(gòu)造函數(shù)的算法不同,需要重載。25.避免對(duì)指針類型和數(shù)值類型進(jìn)行重載
voidf(intx);voidf(string*ps);f(0);//調(diào)用那一個(gè)?調(diào)用f(int)void*constNULL=0;//無(wú)類型指針f(NULL);//錯(cuò)誤類型不符#defineNULL0f(NULL);//調(diào)用f(int)#defineNULL((void*)0))f(NULL);//錯(cuò)誤類型不符classNULLClass//類型名可以隱去{public:template<classT>operatorT*(){return0;}//為任意類型T傳回一個(gè)NULL指針}NULL;f(string*ps);f(NULL);//NULL被轉(zhuǎn)換為string*調(diào)用f(string*ps)盡可能避免對(duì)指針類型和數(shù)值類型進(jìn)行重載26.防備隱性二義性狀態(tài)classB;classA{public:A(constB&);//由B可以造出A來(lái)};classB{public:operatorA()const;//B可以轉(zhuǎn)換成A};voidf(constA&);Bb;f(b);//錯(cuò)誤模棱兩可兩種方法哪種更好?voidf(int);voidf(char);doubled=6.02;f(d);//模棱兩可模棱兩可可以潛伏很久,直到爆發(fā)。多繼承最容易引發(fā)模棱兩可。classB{public:Bdoit();};classC{public:Cdoit();//放在私有成員中同樣不行};classDerived:publicB,publicC{……};Derivedd;d.doit();//模棱兩可d.B::doit();//正確d.C;;doit();//正確27.如果不想使用編譯器暗自產(chǎn)生的成員函數(shù),明確地拒絕
不允許一個(gè)函數(shù)存在,只要不把它放進(jìn)class中。但賦值函數(shù),拷貝構(gòu)造函數(shù)例外,系統(tǒng)會(huì)自行產(chǎn)生一個(gè)這種函數(shù)。不許對(duì)象調(diào)用某個(gè)函數(shù),把它放在私有成員中。但公有函數(shù),友元可以調(diào)用。聲明一個(gè)函數(shù),而不定義它,調(diào)用它編譯器會(huì)指出錯(cuò)誤。28.嘗試切割globalnamespace
(全局命名空間)標(biāo)識(shí)符重名會(huì)引起混亂。同類名詞冠以同一詞頭會(huì)使名字太長(zhǎng)。建議使用namespace名字空間namespacesdm{constintBOOK_VERSION=2.0;classHandle{……};HandlegetHandle();}有三種方法取用namespace內(nèi)的名字。voidf1(){usingnamespacesdm;//匯入所有名字
cout<<BOOK_VERSION;Handleh=getHandle();……}voidf2(){usingsdm::BOOK_VERSION;//匯入單個(gè)名字
cout<<BOOK_VERSION;//正確
Handleh=getHandle();//錯(cuò)誤
……}voidf3(){cout<<sdm::BOOK_VERSION;//沒問題只用一次
doubled=BOOK_VERSION;//錯(cuò)誤
Handleh=getHandle();//錯(cuò)誤
……}兩個(gè)namespace中有相同的名字,只要標(biāo)明namespace域名即可區(qū)分。
usingnamespacestm;usingnamespacesdm;
stm:;BOOK_VERSION;
sdm::BOOK_VERSION;類與函數(shù)的實(shí)現(xiàn)
29.避免傳回內(nèi)部數(shù)據(jù)的handlesclassString{public:String(constchar*value);~String();operatorchar*()const;private:char*data;};
inlineString::operatorchar*()const{returndata;}//潛伏著危險(xiǎn)constStringB(“Iloveyou!”);char*str=B;//str與B.data指向同一個(gè)地址strcpy(str,“Iloveyou?”);//改變str,也就改變了B.data字符串常量B被改變。去掉operatorchar*()const;中const,可以令常量B不能調(diào)用operatorchar*();但String便不能轉(zhuǎn)換成char*.安全的做法是:inlineString::operatorchar*()const{char*copy=newchar[strlen(data)+1];strcpy(copy,data);returncopy;}這個(gè)函數(shù)比較慢,可能產(chǎn)生內(nèi)存丟失。classString{public:String(constchar*value);~String();operatorconstchar*()const;private:char*data;};
inlineString::operatorconstchar*()const{returndata;}//又快又安全傳回一個(gè)常量指針,不可改變。引用也可能發(fā)生傳回內(nèi)部數(shù)據(jù)的handle問題。classString{public:……char&operator[](intindex)const{returndata[index];}private:char*data;};Strings=“Iamnotconst”;s[0]=‘x’;constStringcs=“Iamconst”;cs[0]=‘x’;//改變了常字符串,編譯器沒發(fā)現(xiàn)問題出在const函數(shù)返回值是引用。即使不是const函數(shù),“傳回handles”也是有問題的。//隨機(jī)選擇一個(gè)作家的名字StringsomeFamousAuthor(){switch(rand()%3){//隨機(jī)函數(shù)<stdlib.h>case0:return“MargaretMitchell”;//《飄》的作者
case1:return“StephenKing”;//著名通俗小說(shuō)家
case3:return“ScottMeyers”//本書作者
}return“”;}這個(gè)函數(shù)的返回值是一個(gè)局部指針handle,函數(shù)用畢被銷毀因此返回值將是無(wú)定義的內(nèi)存(danglingpointer)。
盡可能避免讓一個(gè)函數(shù)傳回danglinghandles,可以提高程序的可靠性。避免寫出成員函數(shù)返回一個(gè)
non-constpointer或reference(引用)
并以之指向較低存取層級(jí)的成員classAddress{……};classPerson{public:address&personAddress(){returnaddress;}……private:Addressaddress;//私有數(shù)據(jù)成員……};Personscott(……);Address&addr=scott.personAddress();//全局對(duì)象addr與scott.address同一地址私有成員公開化,可以從外部改變私有數(shù)據(jù)的值。變成指針也有同樣的問題。31.千萬(wàn)不要返回“函數(shù)內(nèi)局部對(duì)象的reference”或“函數(shù)內(nèi)以new獲得的指針?biāo)傅膶?duì)象”
“傳回一個(gè)引用(reference),指向局部對(duì)象”,函數(shù)用畢返回時(shí),局部對(duì)象被析構(gòu),變成引用一個(gè)不存在的對(duì)象。constcomplex&operator*(constcomplex&lhs,constcomplex&rhs){complextemp(lhs.x*rhs.x-lhs.y*rhs.y,lhs.x*rhs.y+lhs.y*rhs.x);returntemp;}錯(cuò)誤。返回值地址指向局部對(duì)象,與局部對(duì)象同名,運(yùn)算執(zhí)行完畢,局部對(duì)象被析構(gòu),返回值指向一個(gè)不存在的地址。classAddress{……};classPerson{public:address&personAddress(){returnaddress;}……private:Addressaddress;//私有數(shù)據(jù)成員……};Personscott(……);Address&addrPtr=scott.personAddress();//指針addr指向scott.address同一地址只要把函數(shù)返回值改成const類型即可避免外部修改。classAddress{……};classPerson{public:address*personAddress(){returnaddress;}……private:Addressaddress;//私有數(shù)據(jù)成員……};Personscott(……);Address*addrPtr=scott.personAddress();//指針addr指向scott.address同一地址同樣問題只要把函數(shù)返回值改成const類型即可避免外部修改。32.盡可能延緩變量定義式的出現(xiàn)//這個(gè)函數(shù)太早定義變量encryptedstringencryptPassward(conststring&passward){stringencrypted;if(passward.length()<MINIMUM_PASSWARD_LENGTH){throwlogic_error(“Passwardistooshort”);……returnencrypted;}一旦有異常拋出,encrypted定義就是多余的。盡量在變量定義時(shí)初始化。33.明智地運(yùn)用inlineinline內(nèi)聯(lián)函數(shù)提高效率,編譯時(shí)實(shí)現(xiàn)最佳化。但是增加object目標(biāo)代碼,加大內(nèi)存開銷。太多的inline函數(shù),會(huì)減低取出指令的速度(instructionfetch)編譯器會(huì)因某些理由自動(dòng)拒絕inline函數(shù),將它當(dāng)成非inline函數(shù)。這時(shí)系統(tǒng)發(fā)出一個(gè)警告。比如太長(zhǎng)的函數(shù),virtual函數(shù),不適合inline的函數(shù)。構(gòu)造函數(shù)看起來(lái)不長(zhǎng),有時(shí)繼承派生類中的構(gòu)造函數(shù),比看起來(lái)的要長(zhǎng)。比如基類的構(gòu)造函數(shù),new的使用,都使inline失效。inline函數(shù)不會(huì)自動(dòng)升級(jí),程序一旦被改動(dòng)就要重新編譯。inline函數(shù)應(yīng)限制用于一些非常平凡的,占有重要效率地位的函數(shù)。慎重使用inline函數(shù),便于日后除錯(cuò)。34.將文件之間的編譯關(guān)系降到最低
一個(gè)class在文件file中定義并實(shí)現(xiàn)。程序用到這個(gè)類的對(duì)象就要連接文件file#include<file.h>file文件就與程序發(fā)生依賴關(guān)系。修改file文件,會(huì)引起全程序重編譯。C++一個(gè)classB中,用到另一classA的對(duì)象,A必須先完全定義并實(shí)現(xiàn)。如果A寫在文件file里,A的任何改變都會(huì)引起整個(gè)程序重新編譯。改進(jìn)的方法是A中盡量使用指向B類對(duì)象的指針或引用。這樣,B只要先聲明,A的編譯不依賴于B.file改變時(shí),A不需要重新編譯。A稱為Handleclass另一種方法是用抽象類做基類,成為Protocolclass。Protocolclass沒有任何實(shí)現(xiàn)。其作用只是一個(gè)接口。如果引用或指針能夠完成任務(wù),就不用對(duì)象。如果可能,以class的聲明,代替class的實(shí)現(xiàn)。盡可能只把class的聲明放在頭文件里,其余實(shí)現(xiàn)放在由客戶完成的文件里,不參與編譯。頭文件盡量不include別的頭文件,除非不聯(lián)不行。繼承關(guān)系與面向?qū)ο笤O(shè)計(jì)繼承體系是C++與C的更本區(qū)別。如果需要一群class,擁有許多共享性質(zhì),那就要考慮用基類還是模板。如果classA是根據(jù)classB實(shí)現(xiàn),考慮A中應(yīng)該有一個(gè)B類對(duì)象還是A繼承B.
如果需要設(shè)計(jì)一個(gè)安全類型,通用,而C++標(biāo)準(zhǔn)庫(kù)未定義,那么應(yīng)該使用模板還是以泛型void*指針來(lái)實(shí)現(xiàn)?“說(shuō)出你的意思,并了解你所說(shuō)的每一句話?!?5.公有繼承,“isa”的關(guān)系
請(qǐng)牢記:公有繼承publicinheritance是一種isa的關(guān)系。如果classD公有繼承classB,則D是B的subclass子類。D的對(duì)象是B的對(duì)象,反之不成立。B比D更一般化,D比B更特殊化??梢杂肂對(duì)象的地方,D的對(duì)象也可以用。要用D對(duì)象的地方,B對(duì)象無(wú)法效勞。每一匹白馬都是馬,每一匹馬不一定是白馬。公孫策“白馬非馬”,白馬是馬的真子集,而不相等。馬是基類,白馬是派生類。C++的繼承關(guān)系,不同于日常生活,也不同于數(shù)學(xué)。鳥bird會(huì)飛。企鵝penguin是鳥,可企鵝不會(huì)飛。如果鳥class中有fly,則企鵝不是鳥的派生類。長(zhǎng)方形
正方形?還是正方形
長(zhǎng)方形??jī)烧叨疾粚?duì)。另外定義一個(gè)基類,長(zhǎng)方形和正方形都是它的派生類。36.區(qū)分接口繼承(interfaceinheritance)和實(shí)現(xiàn)繼承(implementationinheritance)成員函數(shù)的接口總是會(huì)被繼承聲明純虛函數(shù)是為了讓導(dǎo)出類只繼承其接口。純虛函數(shù)也可以定義,即可以提供其實(shí)現(xiàn)代碼。只有一種方法調(diào)用就是寫明class域名.聲明一般虛函數(shù)(非純)是為了讓導(dǎo)出類只繼承其接口和
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 綜合校準(zhǔn)系統(tǒng)戰(zhàn)略市場(chǎng)規(guī)劃報(bào)告
- 《生物化學(xué)》課程標(biāo)準(zhǔn)
- 冷庫(kù)貨物儲(chǔ)存合同范本
- 辦公材料訂購(gòu)合同范本
- 化工空調(diào)采購(gòu)合同范本
- 個(gè)人自我反省檢討書
- 個(gè)人工作犯錯(cuò)檢討書
- 口腔治療合同范本
- 單位承包小區(qū)合同范例
- 養(yǎng)生館招募合伙人合同范本
- 2024-2025學(xué)年六年級(jí)數(shù)學(xué)人教版上冊(cè)寒假作業(yè)(綜合基礎(chǔ)復(fù)習(xí)篇含答案)
- DB33T 1134-2017 靜鉆根植樁基礎(chǔ)技術(shù)規(guī)程
- 樓梯塑料滴水線施工方案
- 《用電檢查與稽查》課件
- 心理健康主題班會(huì)課件73
- 缺血性心臟病麻醉
- 丙戊酸鈉與中樞神經(jīng)系統(tǒng)損傷保護(hù)的研究進(jìn)展
- 小紅書的運(yùn)營(yíng)技巧培訓(xùn)
- 員工上下班交通安全培訓(xùn)課件
- 2024年全國(guó)職業(yè)院校技能大賽中職組(短視頻制作賽項(xiàng))考試題庫(kù)-下(多選、判斷題)
- 《ISO 41001-2018 設(shè)施管理- 管理體系 要求及使用指南》專業(yè)解讀與應(yīng)用指導(dǎo)材料之9:“6 策劃-6.2 設(shè)施管理目標(biāo)及其實(shí)現(xiàn)的策劃”(雷澤佳編制-2024)
評(píng)論
0/150
提交評(píng)論