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

下載本文檔

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

文檔簡介

第6章異常和錯誤6.1異常與bug

6.2異常的體系結構6.3使用異常

6.4調試

本章小結

習題

增強錯誤恢復能力是提高代碼健壯性最有力的途徑之一。遺憾的是,在現(xiàn)實中人們往往忽略錯誤處理情況,就好像程序處在一個無錯的狀態(tài)下進行工作。檢查錯誤是一個乏味的工作,而且會導致代碼膨脹。異常處理是C++具有的一項特點,它能夠使程序中斷并且處理異常情況(通常是錯誤),并且是以一種有序的、有條理的、一致的方式來處理。異常處理允許程序中的一個部分去發(fā)現(xiàn)并分派錯誤情況,而另一部分去處理錯誤。通常是某種類的代碼,也許是某個庫里的類和函數(shù),能夠監(jiān)測出錯誤而不需要知道正確的處理策略。同樣,另一種類的代碼可以處理錯誤而不需要具有監(jiān)測錯誤的功能。C++的異常處理機制為程序員提供了一種處理錯誤的方式,使他們能在給定的系統(tǒng)結構中,以最自然的方式去處理這些錯誤。異常機制也使處理錯誤的復雜性更加清晰可見。6.1異?常?與?bug

在設計軟件系統(tǒng)的過程中,處理程序中的錯誤(bug)和其他反常行為是最困難的部分之一。在大型軟件開發(fā)中,錯誤是不可避免的,在設計和實現(xiàn)中,花在測試、查找和修改錯誤上的開銷往往也是最大的。異常就是運行時出現(xiàn)的不正常,例如運行時耗盡了內存或遇到意外的非法輸入,要打開的文件已經不存在等。異常存在于程序的正常功能之外,并要求程序立即處理。在設計良好的系統(tǒng)中,異常是程序錯誤處理的一部分。當程序代碼檢查到無法處理的問題時,異常處理就特別有用。在這種情況下,監(jiān)測出問題的那部分需要一種方法把控制權轉到可以處理這個問題的那部分程序。錯誤監(jiān)測程序還必須指出具體出現(xiàn)了什么問題。

異常處理機制提供程序中錯誤監(jiān)測和錯誤處理部分之間的通信。C++中的異常處理由三方面組成,既throw、try和catch,即拋出異常和捕獲異常,并對異常進行處理。如果在程序的代碼中出現(xiàn)了異常的情況,也就是說,通過當前語境無法獲得足夠的信息以決定應該采取什么樣的措施,程序員可以創(chuàng)建一個包含錯誤信息的對象并把它拋出當前語境,通過這種方式將錯誤信息發(fā)送到更大范圍的語境中。這種方式被稱為“拋出一個異?!保申P鍵字throw表示。如下所示:

classThrowError{constchar*consterrorData;public:ThrowError(constchar*constmsg=0):errorData(msg){}};voidh(){throwThrowError("exceptionhappened!");}intmain(){h();return0;}

上面的代碼中,ThrowError是一個普通的類,它的構造函數(shù)接受一個char*類型的變量作為參數(shù)。在拋出一個異常時,可以使用任意的類型(包括內置類型),但通常應當為拋出的異常創(chuàng)建特定的類。如果在一個函數(shù)內部拋出了異常,這個函數(shù)就會因為拋出異常而退出。如果不想因為一個throw而退出函數(shù),可以在函數(shù)中試圖解決實際產生程序設計問題的地方和可能產生異常的地方設置一個try塊。這個塊被稱為try塊的原因是程序需要在這里嘗試著調用各種函數(shù)。try塊只是一個普通的程序塊,由關鍵字try引導:

try{//有可能產生異常的各種函數(shù)調用

}

使用異常處理時,可以將所有工作放入try塊中,然后在try塊的后面處理可能產生的異常。這樣一來,代碼將更容易編寫和閱讀,因為代碼的設計目標不會被錯誤處理所干擾。當異常被拋出后就會在異常處理器(exceptionhandler)的地方終止,也就是在這個地方異常被處理。程序員需要為每一種要被捕獲的異常設置一個異常處理器。異常處理器緊接著try塊,由關鍵字catch標識:

try{//可產生異常的代碼

}catch(type1lx1){//處理type1類型的異常}catch(type2lx2){//處理type2類型的異常

}//…catch中的參數(shù)也可以省略,異常類型通常提供了對其進行處理的足夠的信息。如果想捕獲所有類型的異常,用省略號代替異常處理器的參數(shù)列表就能實現(xiàn)這一點:

catch(…){//處理所有捕獲的異常

}

由于省略號異常處理器能夠捕獲任何類型的異常,因此最好將它放在異常處理器列表的最后,從而避免它后面的處處理器接受不到任何的異常。由于省略號異常處理器不接受任何參數(shù),因此得不到任何有關異常的信息,也無法知道異常的類型,它是一個全能捕獲者。異常處理器必須緊跟在try塊之后,一旦某個異常被拋出,異常處理機制將會依次按照在原代碼中出現(xiàn)的順序查找最近的異常處理器,一旦找到匹配的異常處理器,就認為該異常已經被處理了而不再繼續(xù)查找下去。這就是異常匹配。捕捉異常與函數(shù)調用相似,捕捉異常需要傳入exception參數(shù),而函數(shù)調用也需要傳入?yún)?shù)。但異常的參數(shù)與函數(shù)參數(shù)有很大區(qū)別:異常的拋出者在拋出異常之后不再擁有程序控制權,異常處理返回之后不再回到拋出地點,而函數(shù)調用則能在函數(shù)體執(zhí)行之后獲得返回參數(shù)繼續(xù)執(zhí)行。

異常捕捉不能進行隱式類型轉換,例如拋出一個int類型的異常不能被double類型的catch捕捉到。只有在繼承關系的異常對象被拋出時,捕捉其父類對象的catch才能夠捕捉該異常。異常的參數(shù)傳遞總是通過拷貝傳遞的。異常拋出者拋出的異常對象首先被拷貝到一個臨時對象中,這個臨時對象隨后被傳給異常處理參數(shù)。因此,如果通過值的方式獲取異常,會發(fā)生兩次拷貝;通過傳引用的方式會發(fā)生一次拷貝;而傳指針的方式則是應該避免使用的。因為指針指向的對象定義在異常拋出者體內,轉到異常處理體時,異常拋出體作用結束,對象被銷毀,因此在異常處理中得到的對象指針指向的是一個已經被銷毀的對象。

傳值方式的異常存在另一個問題:拋出的異常對象如果被一個捕捉父類對象的catch捕捉到,其拷貝僅僅拷貝父類的數(shù)據(jù),在異常處理體中,無法根據(jù)多態(tài)性動態(tài)調用原始對象類的方法?;谝陨侠碛?,C++中最好使用傳引用的異常傳遞方式。有時如果想把異常推到調用鏈的更高層次上去處理,可以重新拋出異常。在這種情況下,省略號異常處理器正好符合這種要求。這種處理方法可以捕獲所有異常,清理相關資源,然后重新拋出該異常,以使得其他地方的異常處理器能夠處理該異常。在一個異常處理器內部,使用不帶參數(shù)的throw語句可以重新拋出異常:catch(…){cout<<"anexceptionwasthrown"<<endl;//釋放資源

throw;

}6.2異常的體系結構標準C++庫定義了自己的異常類,為了方便快捷,讀者可以使用標準C++的異常類,或從它們繼承來派生出自己的異常類。所有的標準異常類都是從exception類派生的。exception類的兩個主要派生類是logic_error和runtime_error,這兩個類在頭文件<stdexcept>中定義。logic_error類用于描述程序中出現(xiàn)的邏輯錯誤,例如傳遞無效的參數(shù)。runtime_error是指運行時發(fā)生的錯誤,這些錯誤由無法預料的事件所造成,例如硬件故障或內存耗盡。標準異常類的繼承關系如表6.1所示。表6.1標準異常類繼承關系

可以把多個異常組成族系。構成異常族系的一些示例有數(shù)學錯誤異常族系和文件處理錯誤異常族系。在C++代碼中把異常組在一起有兩種方式:異常枚舉族系和異常派生層次結構。程序6.1是一個異常枚舉族系的例子。

【程序6.1】enumFileErrors{nonExist,wrongFormat,diskSeekError...};intf(){ try { //... throwwrongFormat; }catch(FileErrorsfe) { switch(fe) { casenonExist: //... casewrongFormat: //... casediskSeekError: //… } } //...}

在try塊中有一個throw,它拋擲一個FileErrors枚舉中的常量。這個拋擲可被catch(FileErrors)塊捕獲到,接著后者執(zhí)行一個switch,對照情況列表,匹配捕獲到的枚舉常量值。上面的異常族系也可按異常派生層次結構來實現(xiàn),如程序6.2所示。

【程序6.2】classFileErrors{};classNonExist:publicFileErrors{};classWrongFormat:publicFileErrors{};classDiskSeekError:publicFileErrors{};intf(){ try { //... throwWrongFormat; } catch(NonExist) { //… } catch(DiskSeekError){ //… } catch(FileErrors) { //… } //…}

上面的各異常處理程序塊定義了針對類NonExist和DiskSeekError的派生異常類對象,針對FileErrors的異常處理,既捕獲FileErrors類對象,也捕獲WrongFormat對象。

異常捕獲的規(guī)則除了前面所說的,必須嚴格匹配數(shù)據(jù)類型外,對于類的派生,下列情況也可以捕獲異常:

(1)異常處理的數(shù)據(jù)類型是公有基類,拋擲異常的數(shù)據(jù)類型是派生類。

(2)異常處理的數(shù)據(jù)類型是指向公有基類的指針,拋擲異常的數(shù)據(jù)類型是指向派生類的指針。對于派生層次結構的異常處理,catch塊組中的順序是重要的。因為“catch(基類)”總能夠捕獲“throw派生類對象”,所以“catch(基類)”塊總是放在“catch(派生類)”塊的后面,以避免“catch(派生類)”永遠不能捕獲異常。6.3使用異常程序6.3以一個計算器例子來說明如何在實際的編程中使用異常。

【程序6.3】#include<iostream>#include<cstdlib>#include<cctype>#include<string>usingnamespacestd;//DeclaretheerrorclassclassError{};//CalculatorclassdeclarationclassCalculator{intpos;stringexpress;intaddsub();intmultdiv();intnumber()throw(Error);public:Calculator(){}intCompute(conststring&str)throw(Error);};//Calculatorclassdefinition//以上為"Calculator.h"頭文件定義

#include"Calculator.h“intCalculator::Compute(conststring&str)throw(Error){ intretn=0; try { pos=0; express=str; retn=addsub();if(pos<express.length()&&express[pos]!='\0') throwError(); } catch(Error) { cout<<'\r'; while(pos--) cout<<''; cout<<"syntaxerror"<<endl<<'\a'; throw; } returnretn;}intCalculator::addsub(){ intretn=multdiv(); while(express[pos]=='+'||express[pos]=='-') { intop=express[pos++]; intopr2=multdiv(); if(op=='+') retn+=opr2; else retn-=opr2; } returnretn;}//Highestprecedence:multiply/divide.intCalculator::multdiv(){ intretn=number(); while(express[pos]=='*'||express[pos]=='/') { intop=express[pos++]; intopr2=number(); if(op=='*') retn*=opr2; else retn/=opr2; } returnretn;}intCalculator::number()throw(Error){ intretn;

if(express[pos]=='(') { pos++; retn=addsub(); if(express[pos++]!=')')//Musthave')' throwError(); }else{ //Extractthenumber. if(!isdigit(express[pos])) throwError(); charans[80]="0"; inti=0; while(isdigit(express[pos])&&pos<express.length()) ans[i++]=express[pos++]; ans[i]='\0'; retn=atoi(ans); }returnretn;}intmain(){ intanswer; do{ //Readanexpression cout<<"Enterexpression(0toquit):"<<endl; stringexpress; cin>>express;try { Calculatorcalc; answer=calc.Compute(express); if(answer!=0) cout<<answer<<endl; } catch(Error) { cout<<"Tryagain"<<endl; answer=1; } }while(answer!=0); return0;}

運行示例結果如下:

程序6.3中,main()函數(shù)從控制臺讀取表達式,插入一個try塊中,并且實例化一個計算器對象。程序調用計算器對象的Compute()函數(shù),把字符串表達式作為一個參數(shù)來進行傳遞。如果一切順利,Compute()函數(shù)將返回一個整數(shù)值,即表達式的結果。如果數(shù)值為0,程序將會終止;否則,程序將顯示這個數(shù)值并且請求下一個要計算的表達式。如果在計算過程中有錯誤發(fā)生,計算器對象就會發(fā)出一個Error異常。main()函數(shù)將捕獲這個異常,顯示“重試”消息,并請求另一個表達式。Compute()成員函數(shù)初始化下標數(shù)據(jù)成員,到表達式字符串中。插入一個try塊中,并且調用位于遞歸下降頂端的addsub()成員函數(shù)。如果上述過程的任何一個步驟碰到了表達式中存在的錯誤,函數(shù)就會發(fā)出一個Error異常,并由Compute()成員函數(shù)所捕獲。當Compute()捕獲到一個異常信息時,就使用當前下標數(shù)據(jù)成員來顯示錯誤是在表達式的何處被發(fā)現(xiàn)的。然后,Compute()函數(shù)重新發(fā)出異常,這樣main()函數(shù)也可以捕獲它了。同樣,如果沒有任何異常發(fā)出,Compute()函數(shù)會返回addsub()成員函數(shù)所返回的數(shù)值。6.4調試一個合格的程序員必須具有很好的調試程序的技能,調試技能的獲得要靠大量的編程經驗。初學編程的人都有很深的體會,有時發(fā)現(xiàn)程序中的錯誤是很難的,所以調試經驗的積累,其重要性甚至超過學習一門語言。不會調試的程序員就意味著他即使會一門語言,也不能編制出好的軟件。調試通常離不開編程工具的支持,即與集成開發(fā)環(huán)境密切相關。下面以VC++開發(fā)環(huán)境為例來介紹如何進行調試。1.設置環(huán)境首先是對編譯器環(huán)境的設置。為了調試一個程序,首先必須使程序中包含調試信息。一般情況下,一個從AppWizard創(chuàng)建的工程中包含的DebugConfiguration自動包含調試信息,但是Debug版本并不是程序包含調試信息的決定因素,程序設計者可以在任意的Configuration中增加調試信息,包括Release版本。為了增加調試信息,可以按照下述步驟進行:打開Projectsettings對話框(可以通過快捷鍵Alt+F7打開,也可以通過IDE菜單Project/Settings打開),選擇C/C++頁,在Category中選擇general,則出現(xiàn)一個DebugInfo下拉列表框,可供選擇的調試信息顯示方式包括:●命令行Projectsettings說明。●無None沒有調試信息?!?/ZdLineNumbersOnly:目標文件或者可執(zhí)行文件中只包含全局和導出符號以及代碼行信息,不包含符號調試信息?!?/Z7C7.0-Compatible:目標文件或者可執(zhí)行文件中包含行號和所有符號調試信息,包括變量名及類型、函數(shù)及原型等。●?/ZiProgramDatabase:創(chuàng)建一個程序庫(PDB),包括類型信息和符號調試信息?!?/ZIProgramDatabaseforEditandContinue:除了前面/Zi的功能外,這個選項允許對代碼進行調試過程中的修改和繼續(xù)執(zhí)行。這個選項同時使#pragma設置的優(yōu)化功能無效。選擇Link頁,選中復選框“GenerateDebugInfo”,這個選項將使連接器把調試信息寫進可執(zhí)行文件和DLL。如果C/C++頁中設置了ProgramDatabase以上的選項,則可以選擇Linkincrementally。選中這個選項,將使程序可以在上一次編譯的基礎上被編譯(即增量編譯),而不必每次都從頭開始編譯。2.設置斷點調試的最基本的方法就是設置斷點。斷點是調試器設置的一個代碼位置。當程序運行到斷點時,程序中斷執(zhí)行,回到調試器。斷點是最常用的技巧。調試時,只有設置了斷點并使程序回到調試器,才能對程序進行在線調試。

(1)設置斷點:可以通過下述方法設置一個斷點。首先把光標移動到需要設置斷點的代碼行上,然后按快捷鍵F9彈出Breakpoints對話框,方法是按快捷鍵Ctrl+B或Alt+F9,或者通過菜單Edit/Breakpoints打開。打開后點擊Breakat編輯框右側的箭頭,選擇合適的位置信息。一般情況下,直接選擇linexxx就足夠了。如果想設置不是當前位置的斷點,可以選擇Advanced,然后填寫函數(shù)、行號和可執(zhí)行文件信息。(2)去掉斷點:把光標移動到給定斷點所在的行,再次按F9鍵就可以取消斷點。同前面所述,打開Breakpoints對話框后,也可以按照界面提示去掉斷點。條件斷點:可以為斷點設置一個條件,這樣的斷點稱為條件斷點。對于新加的斷點,可以單擊Conditions按鈕,為斷點設置一個表達式。當這個表達式發(fā)生改變時,程序就被中斷。

(3)數(shù)據(jù)斷點:數(shù)據(jù)斷點只能在Breakpoints對話框中設置。選擇“Data”頁,就可彈出設置數(shù)據(jù)斷點的對話框。在編輯框中輸入一個表達式,當這個表達式的值發(fā)生變化時,數(shù)據(jù)斷點就到達。一般情況下,這個表達式應該由運算符和全局變量構成。例如,在編輯框中輸入g_bFlag這個全局變量的名字,那么當程序中有g_bFlag=!g_bFlag時,程序就將停在這個語句處。(4)消息斷點:VC也支持對Windows消息進行截獲,截獲方式有兩種:窗口消息處理函數(shù)和特定消息中斷。在Breakpoints對話框中選擇Messages頁,就可以設置消息斷點。如果在Breakpoints對話框中寫入消息處理函數(shù)名,那么每次消息被這個函數(shù)處理,斷點就到達(筆者覺得如果采用普通斷點在這個函數(shù)中截獲,效果應該一樣)。如果在下拉列表框中選擇一個消息,則每次這種消息到達,程序就中斷。

3.Watch監(jiān)視

VC支持查看變量、表達式和內存的值。所有這些觀察都必須是在斷點中斷的情況下進行。觀看變量的值最簡單,當斷點到達時,把光標移到這個變量上,停留一會就可以看到變量的值。VC提供一種被稱為Watch的機制來觀看變量和表達式的值。在斷點狀態(tài)下,在變量上單擊右鍵,選擇QuickWatch,就彈出一個對話框,顯示這個變量的值。單擊Debug工具條上的Watch按鈕,就出現(xiàn)一個Watch視圖(Watch1,Watch2,Watch3,Watch4),在該視圖中輸入變量或者表達式,就可以觀察它們的值。注意:這個表達式不能有副作用,例如++運算符絕對禁止用于這個表達式中,因為這個運算符將修改變量的值,導致軟件的邏輯被破壞。

Watch監(jiān)視有以下三種:

1)Memory(內存)監(jiān)視由于指針指向的是數(shù)組,Watch只能顯示第一個元素的值。為了顯示數(shù)組的后續(xù)內容,或者要顯示一片內存的內容,可以使用Memory功能。在Debug工具條上點擊Memory按鈕將彈出一個對話框,在其中輸入地址,就可以顯示該地址指向的內存的內容。

2)?Varibles(變量)監(jiān)視單擊Debug工具條上的Varibles按鈕,將彈出一個對話框,顯示所有當前執(zhí)行上下文中可見的變量的值。特別是當前指令涉及的變量將以紅色顯示。

3)寄存器監(jiān)視單擊Debug工具條上的Reigsters按鈕,將彈出一個對話框,顯示當前所有寄存器的值。4.進程控制

VC允許被中斷的程序繼續(xù)運行、單步運行和運行到指定光標處,分別對應快捷鍵F5、F10/F11和Ctrl+F10。各個快捷鍵的功能如下:

F5:繼續(xù)運行。

F10:單步,如果涉及到子函數(shù),不會進入子函數(shù)內部。

F11:單步,如果涉及到子函數(shù),將進入子函數(shù)內部。

Ctrl+F10:運行到當前光標處。

5.調用堆棧調用堆棧(CallStack)反映了當前斷點處函數(shù)是被哪些函數(shù)按照什么順序調用的。單擊Debug工具條上的CallStack按鈕,將顯示CallStack對話框。在CallStack對話框中顯示了一個調用系列,最上面的是當前函數(shù),往下依次是調用函數(shù)的上級函數(shù)。單擊這些函數(shù)名可以跳到對應的函數(shù)中。

6.其他調試手段系統(tǒng)提供一系列特殊的函數(shù)或者宏來處理Debug版本相關的信息,如下:

TRACE:使用方法和printf完全一致,它在output框中輸出調試信息。

ASSERT:它接收一個表達式,如果這個表達式為TRUE,則無動作;否則,中斷當前程序的執(zhí)行。對于系統(tǒng)中出現(xiàn)這個宏導致的中斷,應該認為你的函數(shù)調用未能滿足系統(tǒng)調用此函數(shù)的前提條件。例如,對于一個還沒有創(chuàng)建的窗口調用SetWindowText等。VERIFY和ASSERT功能類似,所不同的是,在Release版本中,ASSERT不計算輸入的表達式的值,而VERIFY計算表達式的值。一個好的程序員不應該把所有的判斷交給編譯器和調試器,應該在程序中自己加以程序保護和錯誤定位,具體措施包括:

(1)對于所有有返回值的函數(shù),都應該檢查返回值,除非你確信這個函數(shù)調用絕對不會出錯,或者不關心它是否出錯。

(2)一些函數(shù)返回錯誤,需要用其他函數(shù)獲得錯誤的具體信息。例如accept返回INVALID_SOCKET表示accept失敗,為了查明具體的失敗原因,應該立刻用WSAGetLastError獲得錯誤碼,并針對性地解決問題。(3)有些函數(shù)通過異常機制拋出錯誤,應該用TRY-CATCH語句來檢查錯誤。程序員對于能處理的錯誤,應該自己在底層處理;對于不能處理的,應該報告給用戶讓他們決定怎么處理。如果程序出了異常,卻不對返回值和其他機制返回的錯誤信息進行判斷,只會加大找錯誤的難度。另外,VC中要編制程序不應該一開始就寫cpp/h文件,而應該首先創(chuàng)建一個合適的工程。因為只有這樣,VC才能選擇合適的編譯、連接選項。對于加入到工程中的cpp文件,應該檢查是否在第一行顯式地包含stdafx.h頭文件,這是MicrosoftVisualStudio為了加快編譯速度而設置的預編譯頭文件。在這這個#include“stdafx.h”行前面的所有代碼將被忽略,所以其他頭文件應該在這一行后面被包含。對于.c文件,由于不能包含stdafx.h,因此可以通過Projectsettings把它的預編譯頭設置為“不使用”,方法是:首先打開Projectsettings對話框,然后選擇C/C++,接著在Category中選擇PrecompilationHeader,最后選擇不使用預編譯頭。本章小結為了檢測異常,程序中使用try、catch和throw語句。異常處理使程序中錯誤的檢測簡單化,并提高了程序處理錯誤的能力。編程技巧與注意事項:

(1)所謂異常是指程序中有運行錯誤,程序應能檢測錯誤;

(2)?try語句使C++能夠進行異常檢測;

(3)?catch緊跟在try語句后面,以捕獲異常;

(4)通過程序中的throw語句報告異常;

(5)異常通過拋出一個類型和catch的參數(shù)相匹配而捕獲;(6)捕獲異常后,程序將執(zhí)行catch中的語句;

(7)如果程序拋出一個不能捕獲的異常,C++將執(zhí)行默認異常處理函數(shù);

(8)調試是編程中非常重要的一項技能,找到程序中的錯

溫馨提示

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

評論

0/150

提交評論