版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認(rèn)領(lǐng)
文檔簡介
1、改善程序設(shè)計技術(shù)的50個有效做法 第二版2002.3 Scott Meyers 侯 捷 譯,如何完成較好的設(shè)計如何避免常見的問題如何提高效率的一些準(zhǔn)則不是放之四海而皆準(zhǔn)的唯一真理,C++新標(biāo)準(zhǔn)新的類型bool,有兩個值true, false.typedef int bool;const bool false=0;const bool true=1;,新的轉(zhuǎn)型動作,stat
2、ic_cast(expression) //將表達式expression轉(zhuǎn)為type類型const_cast(expression) //將常數(shù)類型expression轉(zhuǎn)為非常數(shù)類型dynamic_cast(expression) //安全向下轉(zhuǎn)型 見39reinterpret_cast(expression) //函數(shù)指針類型轉(zhuǎn)換 不常用,1. 盡量以const和inline取
3、代#define,#define 是一個宏,只能被預(yù)處理,而不被編譯,用它定義的常量甚至不被編譯器看見,因此不能發(fā)現(xiàn)使用中的錯誤。用#define定義一個簡單函數(shù),必須為每一個參數(shù)加上一個括號,容易造成錯誤。用內(nèi)聯(lián)函數(shù)高效準(zhǔn)確。,#define ratio 1.653//編譯器看不見ratio,只看見1.653//一旦出錯,不會報告,const double ratio=1.653;const char* const
4、 name=“Scott Meyers”;//字符串常量,In Class 常量,用靜態(tài)變量類內(nèi)聲明,類外定義。class EngineerConstants{ private: static const double Factor;……} ;const double EngineerConstants::Factor=1.35;,2. 盡量以取代,scanf printf 函數(shù) 不能擴充用來輸入輸出自
5、定義類型的變量。cin>>i>>x;cout<<i<<x;可以擴展,方便得多,改變舊有的C習(xí)慣(shifting from C to C++)盡量以const和inline取代#define#define 是一個宏,只能被預(yù)處理,而不被編譯,用它定義的常量甚至不被編譯器看見,因此不能發(fā)現(xiàn)使用中的錯誤。用#define定義一個簡單函數(shù),必須為每一個參數(shù)加上一個括號,容易造成錯
6、誤。用內(nèi)聯(lián)函數(shù)高效準(zhǔn)確。,3. 盡量以new和delete取代malloc和free,malloc 和free 不能調(diào)用構(gòu)造函數(shù),析構(gòu)函數(shù) new 和delete則可。不能混用new delete malloc free 必要用C庫函數(shù)時檢查是否用到malloc重新用new和delete改過。,4. 盡量使用C++風(fēng)格的注釋形式,/* …… */ 要保證成對出現(xiàn),不小心錯一大片。 // 好看好讀 可
7、以混合使用當(dāng)心!#define light_speed 3e8 //m/sec(in a vacum),內(nèi)存管理(memory management),new 隱式調(diào)用構(gòu)造函數(shù),delete 隱式調(diào)用析構(gòu)函數(shù), 可以重載operator new和operator delete. 不小心運用new 和delete會導(dǎo)致各種錯誤。,5. 使用相同形式的new和delete,string *a=new string[
8、10];……delete a;//出錯delete[ ] a;//正確string *b=new string;……delete[ ] b;//出錯delete b;//正確,typedef string addresslines[4];,string *a=new addresslines;……delete a;//出錯delete[ ] a;//正確不要對數(shù)組類型用typedef, 不容易記住用哪一種de
9、lete,6. 記得在析構(gòu)函數(shù)中以delete對付指針成員,如果類中有指針數(shù)據(jù)成員,在每個構(gòu)造函數(shù)中為指針成員配置內(nèi)存,否則將它初始化為0(NULL指針)。若構(gòu)造函數(shù)中用new配置了內(nèi)存,一定要 在析構(gòu)函數(shù)中用delete釋放在賦值運算重載時要將原有指針的內(nèi)存刪除,重新分配內(nèi)存。要在析構(gòu)函數(shù)中刪除這個指針。,不要用delete刪除一個未完成初始化的指針,不要刪除一個未分配內(nèi)存的指針。不要delete從一個類外面
10、傳來的指針。,7. 為內(nèi)存不足的狀況預(yù)作準(zhǔn)備,不能認(rèn)為“檢查內(nèi)存是否分配成功” 是多此一舉。否則會出現(xiàn)嚴(yán)重后果。 必須建立一個錯誤處理策略。當(dāng)operator new 無法滿足需求時,在拋出異常之前,會調(diào)用一個內(nèi)存不足處理函數(shù)new handler,這個函數(shù)由頭文件提供。,typedef void (*new_handle)( );,new_handler set_new_handler(new_handler p)throw
11、( ); new_handler是一個函數(shù)指針,無參, 無返回值。函數(shù)set_new_handler用來配置new_handler,參數(shù)和返回值都是函數(shù)指針,new_handler類型。它確定新的new_handler函數(shù)(參數(shù)),保留舊的new_handler函數(shù)(返回)。,可以自定義新的new_handler函數(shù),用set_new_handler確認(rèn)。,void nomoreMemory( ){ cerr<
12、< “Unable to satisfy for memeory\n” abort( );//exit }int main( ){ set_new_handler(nomoreMemory); int *pBigDataArray = new int[100000000]; ……}當(dāng)operator new 無法配置10000000個整數(shù)空間時,系統(tǒng)調(diào)用nomoreMemory,然后結(jié)束。,設(shè)計new_ha
13、ndler函數(shù),令其完成如下任務(wù):,-- 讓更多的內(nèi)存可用。預(yù)留一塊內(nèi)存, new_handler第一次被調(diào)用時釋放, 同時發(fā)出警告。安裝新的new_handler以取代自己。new_handler中調(diào)用C++標(biāo)準(zhǔn)庫函數(shù)set_new_handler即可。卸載這個new_handler,返回NULL指針,并拋出bad_alloc (或其繼承)類型的異常。直接調(diào)用abort或exit終止程序。
14、,C++不支持class中專用的new_handler,但仍可以在一個class中重載operator new, 和set_new_handler函數(shù),讓它調(diào)用特定的new_handler函數(shù),而不用系統(tǒng)給出的全局new_handler。,class X{ public: static new_handler set_new_handler(new_handler p); static void* operator new
15、(size_t siz); private: static new_handler currentHandler;};,new_handler X::currentHandler;//初始化為0new_handler X::set_new_handler(new_handler p){ new_handler oldHandler = currentHandler; //保留當(dāng)前new_ha
16、ndler currentHandler = p;//再設(shè)置當(dāng)前new_handler return oldHandler;},void *X::operator new(size_t size),{ newHandler globalHandler= std::set_new_handler(currentHandler); //配置新new_handler
17、 保存globalHandler void *memory; try{ memory = ::oprator new(size); //試分配內(nèi)存 } catch(std::bad_alloc&){ std::set_new_handler(globalHandler); //恢復(fù)原有處理方法
18、 throw; //傳播異常 } std::set_new_handler(globalHandler): //恢復(fù)原有處理方法 return memory; }//調(diào)用一次特定處理方法,用畢恢復(fù),//應(yīng)用,void nomoreMemory( );X::set_new_handler(nomoreMemory
19、);X *px1 = new X; //如果內(nèi)存分配失敗,調(diào)用nomoreMemory( )string *ps = new string; //如果內(nèi)存分配失敗,調(diào)用globalHandlerX:: set_new_handler(0); X *px2 = new X; //如果內(nèi)存分配失敗,立即拋出異常,可以做一個混合風(fēng)格基類允許 “設(shè)定class專屬new_hand
20、ler”,templateclass NewHandlerSupport{ public: static new_handler set_ new_handler(new_handler p); static void* operator new(size_t siz); private: static new_handler currentHandler;};,template,new_hand
21、ler NewHandlerSupport:: set_new_handler(new_handler p){ new_handler oldHandler = currentHandler;//保留當(dāng)前new_handler currentHandler = p;//再設(shè)置當(dāng)前new_handler return oldHandler;},template,void * NewHandlerS
22、upport::operator new(size_t size) { newHandler globalHandler= std::set_new_handler(currentHandler); //配置新new_handler 保存globalHandler void *memory; try{ memory = ::oprator new(size
23、); //試分配內(nèi)存 } catch(std::bad_alloc&){ std::set_new_handler(globalHandler); //恢復(fù)原有處理方法 throw; //傳播異常 } std::set_new_handler(globalHandler): //恢復(fù)原有處理方法 return memory;
24、 },new_handler NewHandlerSupport ::currentHandler; //初始化為0,class X : public NewHandlerSupport{ …… //不必聲明set_new_handler和operator new}類X 不必改動原有的程序代碼,就可以繼續(xù)運作。,1993年前C++要求operator new
25、在無法滿足內(nèi)存需求時返回0,新標(biāo)準(zhǔn)則是拋出一個bad_alloc類型異常。失敗便轉(zhuǎn)為0的傳統(tǒng)被保留為“nothrow”不拋出異常。頭文件中定義了一個nothrow對象 class Widget{……}; Widget *pw1 = new Widget; //如果失敗拋出std::bad_alloc異常 if(pw1==0)……//無效 widget *wp2
26、 = new(nothrow) Widget; //如果失敗,返回0 if(wp2==0)……//有效,8. 撰寫operator new和operator delete時應(yīng)遵守的公約,當(dāng)你有必要重載operator new時,你的new函數(shù)的行為應(yīng)該與系統(tǒng)原有的new函數(shù)的行為保持一致。應(yīng)該有正確的返回值:返回一個指針指向分配的內(nèi)存。如果內(nèi)存不足,拋出一個bad_alloc類型的異常
27、。不能覆蓋系統(tǒng)原有的new函數(shù)。,//new 函數(shù)的偽碼,void * operator new(size_t size){ if(size==0){size=1;} //將0內(nèi)存需求,看成1內(nèi)存需求,避免與無內(nèi)存混淆 while(true)//無窮循環(huán) 直到內(nèi)存被分配 或拋出異常 { attempt to allocate size bytes; if(the allocatio
28、n was successful) return (a pointer to the memory); new_handle globalHandle = set_new_handler(0) //利用NULL,找出目前的錯誤處理函數(shù) set_new_handler(globalHandler); //重新設(shè)定為原本的函數(shù) if
29、(globalHandler) (*globalHandler)( ) else throw std::bad_alloc( ); }},無窮循環(huán)可以讓更多的內(nèi)存可用,或安裝一個不同的new_handler,或卸載new_handler,或拋出一個異常,或直接結(jié)束程序。,operator new 可以被繼承, 但要小心,否則會導(dǎo)致問題,class Base{ public: static
30、void*oprator new(size_t size); …… };class Derived : public Base{……};//導(dǎo)出類中沒有operator new 函數(shù)Derived *p = new Derived; //調(diào)用Base類中的operator new出錯這里導(dǎo)出類內(nèi)存比基類要大。,改進的辦法:,void *operator new(size_t size){…
31、… if(size != sizeof (Base) return :: oprator new(size); //回到標(biāo)準(zhǔn)operator new函數(shù) ……},重寫operator delete,void operator delete(void*rawMemory){ if(rawMemory == 0)return;
32、 //與C++標(biāo)準(zhǔn)delete保持一致 Deallocate the memory pointed to by rawMemory; return;},//member版,class Base{ public: static void *operator new(size_t size); static void operator delete(void* rawMemory,s
33、ize_t size); ……},void Base::operator delete( void* rawMemory,size_t size);{ if(rawMemory ==0)return; if(size!=sizeof(Base) //如果大小錯誤 { :: operator delete(rawMemory); //用標(biāo)準(zhǔn)
34、版delete處理 return; } deallocate the memory pointed to by rawmeMemory; return;},9. 避免覆蓋new的正規(guī)形式,解決辦法 (1) 再寫的一個專用的operator new函數(shù),讓它支 持正規(guī)的new class X{ public: void f( );
35、 static void* operator new(size_t size, new_handler p); static void* operator new(sise_t size) { return ::operator new(si
36、ze); }};,X *p1=new (specialErrorHandler) X;//調(diào)用X:: operator new(size_t size, new_handler p);X *p2=new X;//調(diào)用X:: operator new(size_t size); (2) 為operator new的每一個參數(shù)提供默認(rèn)值 (缺省值),10. 如果寫了一個operator new
37、 不要忘記寫一個operator delete,需要動態(tài)分配大量小額內(nèi)存空間的應(yīng)用程序,有時需要重載operator new。class AirplaneRep {……};class Airplane { public:……private: AirplaneRep *rep; //唯一數(shù)據(jù)成員是指針};,Airplane *p=new Airplane; //要求內(nèi)存不大,分配的內(nèi)存比實際所需要的內(nèi)存要大
38、,這是為了delete 這塊內(nèi)存時,系統(tǒng)能知道其大小。,pa,,為了節(jié)省內(nèi)存需要定制內(nèi)存管理。,定制內(nèi)存管理。,class Airplane{ public: static void* operator new( size_t size); static void operator delete(void*deadObject, size_t size); …… private: union{
39、 AirplaneRep *rep; Airplane *next; };//兩個指針公用一個內(nèi)存 static const int BLOCK_SIZE; static Airplane * headOfFreeList; //用鏈表配置一片內(nèi)存,整個類只須一個鏈};,void* Airplane ::operator new( s
40、ize_t size);{ if(size != sizeof(Airplane)) return ::operator new(size); Airplane *p= headOfFreeList; //p指向鏈表頭 if(p) headOfFreeList = p->next; //表頭后移, p可用 else { A
41、irplane *newBlock = static_cast(::operator new(BLOCK_SIZE*sizeof(Airplane))); for(int i=1; i<BLOCKSIZE-1;++i) //保留第一塊 newBlock[i].next = &newBlock[i+1]
42、; newBlock[BLOCK_SIZE-1].next = 0; //置表尾 p = newBlok; // p可用 headOfFreeList =&newBlock[1]; } return p; },只有當(dāng) ::operator new失敗時,這里的operator new 才失敗。這時::operator new會調(diào)用new_handler直到拋出異常,因此我們不需
43、要再寫一次new_handler處理具體實現(xiàn)文件中要先對靜態(tài)成員初始化, Airplane * Airplane::headOfFreeList; // headOfFreeList置0 const int Airplane::BLOCK_SIZE = 512;這個版本的operator new 可以運作良好,速度快過兩個數(shù)量級。,還要在Airplane類中寫一個
44、operator delete void Airplane::operator delete(void*deadObject, size_t size) { if(deadObject==0) return; if(size !=sizeof(Airplane)){ ::operator delete(deadObject);
45、 //與operator new處理保持一致 return; } Airplane *carcass = static_cast(deadObject); carcass->next =headOfFreeList; HeadOfFreeList = carcass;},如果沒有定義相應(yīng)的delete函數(shù),而使用了原有的del
46、ete, 結(jié)果會出現(xiàn)意想不到的錯誤,有時是嚴(yán)重的錯誤。如果用member版本不要忘記定義virtual 析構(gòu)函數(shù)。這里的delete函數(shù)沒有memory leak 問題。 這是因為用了memory pool 一次分配一塊內(nèi)存,逐步使用逐步釋放,不必再專門釋放memory pool.,定義一個memory pool 類,使每一個pool對象都是一個內(nèi)存配置器。,class Pool{ public: Pool(si
47、ze_t n); void* alloc( size_t n);//為一個對象配置足夠 //的內(nèi)存遵循operator new的規(guī)矩 void free(void* p, size_t n);//將p的內(nèi)存送回 //pool遵循operator delete的規(guī)矩 ~pool( );//釋放pool中所有內(nèi)存};,用
48、Pool 對象來配置內(nèi)存,當(dāng)被銷毀時,配置的內(nèi)存自動被釋放。于是memory leak 就可以避免。,class Airplane{ public: static void* operator new( size_t size); static void operator delete(void*p, size_t size); …… private: AirplaneRep *r
49、ep; static Pool memPool; //Airplane的memory pool };,inline void Airline::operator new(size_t size) { return memPool.alloc(size); }inline void Airline::operator delete(void* p, size_t size)
50、 {memPool.free(p, size); }為Airplane 的memPool初始化, 要放在Airplane 類實現(xiàn)的文件里Pool Airplane::memPool(sizeof(Airplane));,構(gòu)造函數(shù)、析構(gòu)函數(shù)和賦值運算符,構(gòu)造函數(shù)、析構(gòu)函數(shù)和賦值運算用來產(chǎn)生一個新對象并初始化,撤銷一個對象并收回占有的內(nèi)存,為已有的對象賦一個新值。 不能有錯,必須將他們徹底搞清楚。,11. class內(nèi)有成員
51、指針并動態(tài)配置內(nèi)存時,一定要有拷貝構(gòu)造函數(shù),賦值運算符重載,class String{ public: String(const char*value); ~String( ); …… //沒有拷貝構(gòu)造函數(shù), //也沒有賦值運算符重載 private: char*data;};,String::String(const char *value)
52、{ if(value) { data = new char[strlen(value)+1]; strcopy(data, value); } else { data = new char[1]; *data = “\0”; }}inline String::~String( ){delete[ ] data;},String a( “Hello” );String b(“World”
53、 );b = a;,,,Hello,World,a,b,data,data,,,,,,,由于沒有自定義的賦值函數(shù),只能用C++產(chǎn)生的默認(rèn)賦值函數(shù),它簡單地將b的成員指針data指向a.data,引起 字符串“World”占有的內(nèi)存遺失。而且a.data與b.data指向同一個內(nèi)存,其中一個被析構(gòu)時另一個就丟失了。,拷貝構(gòu)造函數(shù)用來傳值,void donothing(String la){ }String s= “the tr
54、uth is out of there”;donothing(s);當(dāng)函數(shù)donothing完成任務(wù)后,參數(shù)s所含的指針被析構(gòu),la被刪除。即便la不再使用,將來又一次析構(gòu)la會造成問題。,解決的辦法就是自己定義拷貝構(gòu)造函數(shù),賦值函數(shù)重載。如果確信永不使用這些函數(shù),把他們定義為私有函數(shù),而且不實現(xiàn)。一旦出錯,編譯器會給出錯誤提示。,12構(gòu)造函數(shù)中盡量以初始化代替賦值,一個類中的const成員數(shù)據(jù)和reference引用數(shù)
55、據(jù)只能被初始化,不能被賦值。即便沒有const成員數(shù)據(jù)和reference引用數(shù)據(jù),初始化也比賦值效率高。構(gòu)造函數(shù)分兩個階段實現(xiàn): 1. 數(shù)據(jù)成員初始化。 2. 調(diào)用構(gòu)造函數(shù)。數(shù)據(jù)成員賦值要調(diào)用構(gòu)造函數(shù),再調(diào)用賦值函數(shù),做兩次調(diào)用影響效率。初始化也容易維護,修改。,有一種例外: 一個類內(nèi)有大量數(shù)據(jù)成員時,賦值比初始化效率高。,class ManyDataMbs{ public: ManyDataMbs
56、() ManyDataMbs(const ManyDataMbs& x); private: int a, b, c, d, e, f, g, h; double i, j, k, l, m; void init ( );//用來將數(shù)據(jù)成員初始化,不做他用};,void ManyDataMbs::init( ){ a=b=c=d=e=f=g=h=1; i=j=k=l=m=0;}ManyDa
57、taMbs::ManyDataMbs( ){ init( ); ……}ManyDataMbs::ManyDataMbs(const ManyDataMbs& x){ init( );……},靜態(tài)數(shù)據(jù)成員static class member不應(yīng)該在構(gòu)造函數(shù)中初始化。靜態(tài)數(shù)據(jù)成員只能初始化一次,不能初始化多次。,12. 數(shù)據(jù)成員初始化的次序應(yīng)該和類內(nèi)聲明的次序相同,templateclass Array //
58、有上下界的數(shù)組{ public: Array(int lowBound, int highBound); …… private: vector data; //數(shù)組數(shù)據(jù)存儲于一個vector對象data中 size_t size; //數(shù)組中元素的個數(shù) int lBound, hBound; //上下界};,templateArray::Array(int lowBound
59、, int highBound): size(highBound-lowBound+1), lBound(lowBound), hBound(highBound), data(size){ }實際初始化中,data先被初始化,然后 size, lBound, hBound.這樣數(shù)組中,究竟有多少個元素?zé)o法確定?;惓蓡T總是比導(dǎo)出類先初始化。多重繼承時初始化的先后次序要十分小心。,14. 總是讓基類擁有虛析構(gòu)函數(shù),一個軍
60、事應(yīng)用軟件class EnemyTarget{ public: EnemyTarget( ){++ numTargets;} EnemyTarget(const EnemyTarget&) {++ numTargets;} ~ EnemyTarget( ){-- numTargets;} static size_t numberOfTargets( ) {return numT
61、argets;} virtual bool destroy( ); //摧毀敵方目標(biāo)是否成功 private: static size_t numTargets; //對象計數(shù)器 };size_t EnemyTarget::numTargets; //靜態(tài)成員初始化為0,放在類外,class EnemyTank : public EnemyTarget{ publ
62、ic: EnemyTank ( ){++ numTanks;} EnemyTank (const EnemyTank&){++ numTanks;} ~ EnemyTank ( ){-- numTanks;} static size_t numberOfTanks( ){return numTanks;} virtual bool destroy( ); //摧毀敵方坦克是否成功 private
63、: static size_t numTanks; //敵方坦克計數(shù)器 };,EnemyTarget *targetPtr = new EnemyTank;……delete targetPtr; //未定義,計數(shù)出錯,影響戰(zhàn)斗勝敗 解決辦法,把EnemyTarget類中的析構(gòu)函數(shù)定義為virtual即可。幾乎所有的基類都有虛函數(shù),只要有一個虛函數(shù),就要把析構(gòu)函數(shù)定義為虛函數(shù)。沒有虛函數(shù)的類,有繼承派生類對象析構(gòu),也
64、要定義虛析構(gòu)函數(shù)。但虛函數(shù)會增加內(nèi)存開銷。完全不必要時不要用虛析構(gòu)函數(shù)。聲明一個抽象類,可以加一個純虛析構(gòu)函數(shù)。,15. 讓operator=返回*this的引用reference,C語言中operator= 的原型C& C::operator=(const C&);char x, y, z;x=y=z= ‘a(chǎn)’;x,operator=(y.operator=(z.operator= ‘a(chǎn)’));z.op
65、erator=的返回值是y.operator=的實參。他們應(yīng)該有相同的類型。但不要讓operator=返回void類型,const類型,Strin& String::operator=(const String& rhs){ …… return *this; //返回一個引用指向左側(cè)對象}Strin& String::operator=(const String& rhs){ …… r
66、eturn rhs; //返回一個引用指向右側(cè)對象,錯誤}后一個返回值,編譯器無法編譯,無法返回const類型. 如果參數(shù)中去掉const變成:Strin& String::operator=( String& rhs);X= ‘a(chǎn)’; //無法編譯 rhs應(yīng)該是一個變量。結(jié)論:必須返回 *this;,16. 在operator=中為所有的數(shù)據(jù)成員賦值,基類中這不成問題,在派生類中要小心。正確的賦值運
67、算Derived& Derived::operator=(const Drived& rhs){ if(this = = &rhs) return *this; Base::operator=(rhs); //調(diào)用基類的賦值運算 data = rhs.data; return *this; },Derived& Derived::operator=(const Drive
68、d& rhs){ if(this = = &rhs) return *this; static_cast(*this) = rhs; //*this強制轉(zhuǎn)換成基類的引用賦值基類成員 data = rhs.data; return *this; } 拷貝構(gòu)造函數(shù)中要調(diào)用基類構(gòu)造函數(shù)。 用第一種方法,在operator=中檢查是否
69、“自己賦值給自己”,class X{……};X a; X&b = a;//b是a的別名 (aliasing)a = b; //自己賦值給自己 合法 在賦值函數(shù)中要特別謹(jǐn)慎的處理自己的別名賦值給自己的問題。,提高效率 先做檢查,一發(fā)現(xiàn)自己賦值給自己立即返回。導(dǎo)出類的賦值運算重載中一定要先檢查,可以節(jié)省許多工作確保正確性 賦值運算通常要先將左邊對象的資源釋放,再行賦值。如果有自己賦值給自己的現(xiàn)象,這
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 眾賞文庫僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 測繪程序設(shè)計技術(shù)基礎(chǔ)
- vb程序設(shè)計例題-程序改錯程序填空程序設(shè)計
- 最簡單的c程序設(shè)計――順序程序設(shè)計
- 程序設(shè)計教案 程序設(shè)計——數(shù)據(jù)結(jié)構(gòu)
- 4最簡單的c程序設(shè)計順序程序設(shè)計1
- 程序設(shè)計基礎(chǔ)教學(xué)資源的有效性評價研究.pdf
- 自動程序設(shè)計
- 894程序設(shè)計
- 程序設(shè)計實習(xí)
- android程序設(shè)計
- 程序設(shè)計類
- java程序設(shè)計
- 5132程序設(shè)計
- 1程序設(shè)計語言1程序設(shè)計語言的分類
- 計算機程序設(shè)計基礎(chǔ)vb語言程序設(shè)計
- 程序設(shè)計教案
- 程序設(shè)計題
- 程序設(shè)計論文
- excelvba程序設(shè)計
- 網(wǎng)絡(luò)程序設(shè)計
評論
0/150
提交評論