C++计算机语言教学课件c++-lect08C++VirtualFunctionsandPolymorphism_第1页
C++计算机语言教学课件c++-lect08C++VirtualFunctionsandPolymorphism_第2页
C++计算机语言教学课件c++-lect08C++VirtualFunctionsandPolymorphism_第3页
C++计算机语言教学课件c++-lect08C++VirtualFunctionsandPolymorphism_第4页
C++计算机语言教学课件c++-lect08C++VirtualFunctionsandPolymorphism_第5页
已阅读5页,还剩53页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

Chapter7-C++VirtualFunctionsandPolymorphismOutline

7.1 Introduction 7.2 TypeFieldsand

switch

Statements 7.3 VirtualFunctions 7.4 AbstractBaseClassesandConcreteClasses

7.5 Polymorphism

7.6 NewClassesandDynamicBinding

7.7 VirtualDestructors

7.8 CaseStudy:InheritingInterfaceandImplementation7.9 Polymorphism,

virtual

FunctionsandDynamic Binding“UndertheHood”8.0 VirtualBaseClassandAbstractBaseClasses

ObjectivesInthischapter,youwilllearn:Tounderstandthenotionofpolymorphism.Tounderstandhowtodefineandusevirtualfunctionstoeffectpolymorphism.Tounderstandthedistinctionbetweenabstractclassesandconcreteclasses.Tolearnhowtodefinepurevirtualfunctionstocreateabstractclasses.Toappreciatehowpolymorphismmakessystemsextensibleandmaintainable.TounderstandhowC++implementsvirtualfunctionsanddynamicbinding“underthehood.”7.1IntroductionvirtualfunctionsandpolymorphismDesignandimplementsystemsthataremoreeasilyextensibleProgramswrittentogenericallyprocessobjectsofallexistingclassesinahierarchy(更通用的)在C++中,多态性(面向对象的精髓)通过虚函数实现。在基类的函数前加上virtual关键字,在派生类中重写该函数,运行时将会根据对象的实际类型来调用相应的函数:如果对象类型是派生类,调用派生类的函数,如果对象类型是基类,调用基类的函数。多态性概述赋值兼容原则:派生类的对象可以被当成基类的对象来使用。派生类的对象可以赋值给基类的对象Baseb;Derived;b=d;派生类的对象可以初始化基类对象的引用Derived;Base&b=d;派生类对象的地址可以赋值给基类的指针Derivedd;Base*bPtr=&d;原因分析:1)派生类是基类的超集,并包含有基类的成员(继承来的)。2)当将派生类的对象赋值给基类的对象时,系统将继承来的成员赋值给基类的对象;3)当将基类的对象赋值给派生类的对象将导致派生类的对象有未被复制成员(成员不够)。内存布局:父类部分内存子类继承部分this指针子类对象的内存weneedtodefineabehaviorforoneobjectaccordingtoitstype7.2TypeFieldsand

switch

Statements

7.2TypeFieldsand

switch

Statements

switchstatement

Takeanactiononanobjectbasedonitstype根据类型来判断哪个行为被调用AswitchstructurecoulddeterminewhichfunctiontocallbasedonwhichtypeinahierarchyProblemswithswitchProgrammermayforgettotestallpossiblecasesinaswitch.Trackingthisdowncanbetimeconsuminganderrorpronevirtualfunctionsandpolymorphicprogrammingcaneliminatetheneedforswitch7.3VirtualFunctionsvirtualfunctionsUsedinsteadofswitchstatementsDefinition:Keyword

virtual

beforefunctionprototypeinbaseclass

virtualvoiddraw()const;Abase-classpointerorreferencetoaderivedclassobjectwillcallthecorrectdrawfunction(在运行时,依据对象的类型(由对象的地址决定)确认调用哪一个draw函数)Ifaderivedclassdoesnotdefinea

virtualfunctionitisinheritedfromthebaseclass如果一个派生类没有定义virtual,就直接继承基类的函数(virtual就跟普通函数一样)7.3VirtualFunctionsForexampleShapePtr->Draw();(指针型调用)CompilerimplementsdynamicbindingFunctiondeterminedduringexecutiontime动态绑定ShapeObject.Draw();CompilerimplementsstaticbindingFunctiondeterminedduringcompile-time静态绑定If引用,满足动态绑定PureVirtualFunctions纯虚函数是一种特殊的虚函数,格式如下,在参数列表后加“=0”:virtual<类型><函数名>(<参数表>)=0;含有(或继承)一个或多个纯虚函数的类是抽象基类。除了作为抽象基类的派生类的对象的组成部分,不能创建抽象类型的对象。引入的原因:在很多情况下,基类本身生成对象是不合情理的。例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理。7.4AbstractandConcreteClassesAbstractclasses抽象类Solepurposeistoprovideabaseclassforotherclasses抽象类纯虚函数:纯粹是规范一个接口NoobjectsofanabstractbaseclasscanbeinstantiatedToogenerictodefinerealobjects,i.e.TwoDimensionalShapeCanhavepointersandreferences可以有指针和引用Concreteclasses-classesthatcaninstantiateobjects具体类Providespecificstomakerealobjects,i.e.Square,Circle提供具体来创建一个真实的对象

7.4AbstractandConcreteClassesMakingabstractclassesDefineoneormorevirtualfunctionsas“pure”byinitializingthefunctiontozero纯虚函数

virtualdoubleearnings()const=0;Purevirtualfunction当存在一个或者多个纯虚函数的类就是抽象类7.4AbstractandConcreteClassesMakingabstractclassesDefineoneormorevirtualfunctionsas“pure”byinitializingthefunctiontozero纯虚函数

virtualdoubleearnings()const=0;Purevirtualfunction除非在派生类中完全实现基类中所有的的纯虚函数,否则,派生类也变成了抽象类,不能实例化对象。抽象类(AbstractClass)解决了什么问题。抽象类是一种特殊的类,它是为了抽象和设计的目的而建立的,它处于继承层次结构的较上层。抽象类是不能定义对象的,在实际中为了强调一个类是抽象类,可将该类的构造函数说明为保护的访问控制权限。抽象类的主要作用是将有关的组织在一个继承层次结构中,由它来为它们提供一个公共的根,相关的子类是从这个根派生出来的。抽象类刻画了一组子类的操作接口的通用语义,这些语义也传给子类。一般而言,抽象类只描述这组子类共同的操作接口,而完整的实现留给子类。

抽象类只能作为基类来使用,其纯虚函数的实现由派生类给出。如果派生类没有重新定义纯虚函数,而派生类只是继承基类的纯虚函数,则这个派生类仍然还是一个抽象类。如果派生类中给出了基类纯虚函数的实现,则该派生类就不再是抽象类了,它是一个可以建立对象的具体类了。

7.4AbstractandConcreteClasses7.5PolymorphismPolymorphism:Abilityforobjectsofdifferentclassestoresponddifferentlytothesamefunctioncall

不同类型不同的响应Base-classpointer(orreference)callsa

virtual

functionC++choosesthecorrectoverriddenfunctioninobjectSupposeprint

notavirtualfunction

Employeee,*ePtr=&e; HourlyWorkerh,*hPtr=&h; ePtr->print();//callbase-classprintfunction

hPtr->print();//callderived-classprintfunction

ePtr=&h;//allowableimplicitconversion

ePtr->print();//stillcallsbase-classprint7.5PolymorphismPolymorphism:Supposeprintisavirtualfunction Employeee,*ePtr=&e; HourlyWorkerh,*hPtr=&h; ePtr->print();//callbase-classprintfunction

hPtr->print();//callderived-classprintfunction

ePtr=&h;//allowableimplicitconversion

ePtr->print();//callderived-classprint7.6NewClassesandDynamicBindingDynamicbinding(latebinding)Object'stypenotneededwhencompilingvirtualfunctionsForexample,wecancompilethefollowingcode.voidprintEmp(Employee&e) {//类型参数可以是Employee或其派生类 Employee*ePtr=&e; ePtr->print();//若print函数为虚函数,则根据参数e的实 //际类型来调用对应的print}Accommodate(允许)newclassesthathavebeenaddedaftercompilationImportantforISV’s(IndependentSoftwareVendors)whodonotwishtorevealsourcecodetotheircustomers可以把具体实现算法的派生类封装到加密的代码库中。7.6NewClassesandDynamicBinding派生类与虚函数派生类一般会重定义所继承的虚函数。派生类没有重定义某个虚函数,则使用基类中定义的版本。派生类中虚函数的声明必须与基类中的定义方式完全匹配,但有一个例外:返回对基类型的引用(或指针)的虚函数。派生类中的虚函数可以返回派生类的引用(或指针)。(?)例如:Employee类可以定义返回Employee*的虚函数,如果这样,HourlyWorker类中的虚函数重写可以定义为返回Employee*或HourlyWorker*。7.7VirtualDestructorsProblem:Ifbase-classpointertoaderivedobjectis

deleted,thebase-classdestructorwillactontheobject注意析构函数的调用Solution:Defineavirtualbase-classdestructorNow,theappropriatedestructorwillbecalled7.7VirtualDestructors删除指向动态分配对象的指针时,需要运行析构函数在释放对象的内存之前清除对象。处理继承层次中的对象时,指针的静态类型可能与被删除对象的动态类型不同——删除实际指向派生类对象的基类类型指针。(?)7.7VirtualDestructors如果删除基类指针,则需要运行基类析构函数并清除基类的成员,如果对象实际是派生类型的,则没有定义该行为。要保证运行适当的析构函数,基类中的析构函数必须为虚函数:classEmployee{public:

//nowork,butvirtualdestructorneeded

//ifbasepointerthatpointstoaderivedobjectisever //deleted

virtual~Employee(){}};7.7VirtualDestructors如果析构函数为虚函数,那么通过指针调用时,运行哪个析构函数将因指针所指对象类型的不同而不同:Employee*itemE=new

Employee();//samestaticanddynamictypedelete

itemE;//ok:destructorforEmployeecalleditemE=new

HourlyWorker();//ok:staticanddynamictypesdifferdelete

itemE;

//ok:destructorforHourlyWorkercalled7.7VirtualDestructors最佳实践:即使析构函数没有工作要做,继承层次的根类也应该定义一个虚析构函数。构造函数和赋值操作符不是虚函数构造函数是在对象完全构造之前运行的,在构造函数运行的时候,对象的动态类型还不完整。每个类有自己的赋值操作符,派生类中的赋值操作符有一个与类本身类型相同的形参。将赋值操作符设为虚函数可能会出错,因为虚函数必须在基类和派生类中具有同样的形参。7.8CaseStudy:InheritingInterfaceandImplementationRe-examinethePoint,Circle,CylinderhierarchyUsetheabstractbaseclassShapetoheadthehierarchy先抽象出通用接口,设计一个基类ShapeDefinition(abstractbaseclass)---------------------1.PointDefinition(derivedclass)1//Fig.7.1:shape.h2//DefinitionofabstractbaseclassShape抽象类3#ifndef

SHAPE_H4#define

SHAPE_H56classShape{7public:8

virtualdoublearea()const{return

0.0;}9

virtualdoublevolume()const{return

0.0;}1011

//purevirtualfunctionsoverriddeninderivedclasses12

virtualvoidprintShapeName()const=0;13

virtualvoidprint()const=0;14};//endclassShape15#endif1.

PointDefinition(derivedclass)1.1FunctionDefinitions18//DefinitionofclassPoint19#ifndef

POINT1_H20#define

POINT1_H2122#include<iostream>2324usingstd::cout;26#include

"shape.h"2728classPoint:publicShape{29public:30Point(int=0,int=0);//defaultconstructor31

voidsetPoint(int,int);32

intgetX()const{returnx;}33

intgetY()const{returny;}34

virtualvoidprintShapeName()const{cout<<"Point:";}35

virtualvoidprint()const;36private:37

intx,y;

//xandycoordinatesofPoint38};//endclassPoint39#endif

1.

PointDefinition(derivedclass)1.1FunctionDefinitions41//Fig.7.1:point1.cpp42//MemberfunctiondefinitionsforclassPoint43#include

"point1.h"4445Point::Point(inta,intb){setPoint(a,b);}4647voidPoint::setPoint(inta,intb)48{49x=a;50y=b;}//endfunctionsetPoint

53voidPoint::print()const

54{cout<<'['<<x<<

","<<y<<']';}1.CircleDefinition(derivedclass)56//DefinitionofclassCircle57#ifndef

CIRCLE1_H58#define

CIRCLE1_H59#include

"point1.h"6061classCircle:publicPoint{62public:63

//defaultconstructor64Circle(doubler=0.0,intx=0,inty=0);6566

voidsetRadius(double);67

doublegetRadius()const;68

virtualdoublearea()const;69

virtualvoidprintShapeName()const{cout<<"Circle:";}70

virtualvoidprint()const;71private:72

doubleradius;//radiusofCircle73};//endclassCircle7475#endif1.1FunctionDefinitions77//MemberfunctiondefinitionsforclassCircle#include<iostream>usingstd::cout;82#include

"circle1.h"8384Circle::Circle(doubler,inta,intb)85:Point(a,b)

//callbase-classconstructor86{setRadius(r);}8788voidCircle::setRadius(doubler){radius=r>0?r:0;}8990doubleCircle::getRadius()const{returnradius;}9192doubleCircle::area()const

93{return

3.14159*radius*radius;}9495voidCircle::print()const96{97Point::print();98cout<<";Radius="<<radius;99}//endfunctionprint1.CylinderDefinition(derivedclass)101//DefinitionofclassCylinder102#ifndef

CYLINDR1_H103#define

CYLINDR1_H104#include

"circle1.h"105106classCylinder:publicCircle{107public:108//defaultconstructor109Cylinder(doubleh=0.0,doubler=0.0,110

intx=0,inty=0);111112

voidsetHeight(double);113

doublegetHeight();114

virtual

doublearea()const;115

virtualdoublevolume()const;116

virtualvoidprintShapeName()const{cout<<"Cylinder:";}117

virtualvoidprint()const;118private:119

doubleheight;//heightofCylinder120};//endclassCylinder122#endif1.1FunctionDefinitions

123//Fig.7.1:cylindr1.cpp124//MemberandfriendfunctiondefinitionsforclassCylinder125#include<iostream>126127usingstd::cout;128129#include

"cylindr1.h"130131Cylinder::Cylinder(doubleh,doubler,intx,inty)132:Circle(r,x,y)

//callbase-classconstructor133{setHeight(h);}1341351.1FunctionDefinitions134135voidCylinder::setHeight(doubleh)136{height=h>0?h:0;}137138doubleCylinder::getHeight(){returnheight;}139140doubleCylinder::area()const141{142

//surfaceareaofCylinder143

return

2*Circle::area()+144

2*3.14159*getRadius()*height;145}//endfunctionarea146Driver1.Loadheaders1.1Functionprototypes

147doubleCylinder::volume()const

148{returnCircle::area()*height;}149150voidCylinder::print()const151{152Circle::print();153cout<<";Height="<<height;

}//endfunctionprint

Driver1.Loadheaders1.1Functionprototypes155//Fig.7.1:fig20_01.cpp156//Driverforshape,point,circle,cylinderhierarchy157#include<iostream>158159usingstd::cout;160usingstd::endl;161162#include<iomanip>163164usingstd::ios;165usingstd::setiosflags;166usingstd::setprecision;167168#include

"shape.h"169#include

"point1.h"170#include

"circle1.h"171#include

"cylindr1.h"1721.2Initializeobjects2.Functioncalls

173voidvirtualViaPointer(constShape*);174voidvirtualViaReference(constShape&);175176intmain()177{178cout<<setiosflags(ios::fixed|ios::showpoint)179<<setprecision(2);180181Pointpoint(7,11);//createaPoint182Circlecircle(3.5,22,8);//createaCircle183

Cylindercylinder(10,3.3,10,10);//createaCylinder184185point.printShapeName();//staticbinding186point.print();

//staticbinding187cout<<'\n';1881981.2Initializeobjects2.Functioncalls188189circle.printShapeName();//staticbinding190circle.print();//staticbinding191cout<<'\n';192193cylinder.printShapeName();//staticbinding194cylinder.print();//staticbinding195cout<<

"\n\n";196197Shape*arrayOfShapes[3];//arrayofbase-classpointers1982.Functioncalls

199

//aimarrayOfShapes[0]atderived-classPointobject200arrayOfShapes[0]=&point;201202

//aimarrayOfShapes[1]atderived-classCircleobject203arrayOfShapes[1]=&circle;204205

//aimarrayOfShapes[2]atderived-classCylinderobject206arrayOfShapes[2]=&cylinder;207208

//LoopthrougharrayOfShapesandcallvirtualViaPointer209

//toprinttheshapename,attributes,area,andvolume210

//ofeachobjectusingdynamicbinding.211cout<<"Virtualfunctioncallsmadeoff"212<<"base-classpointers\n“;2.Functioncalls213214

for(inti=0;i<3;i++)215virtualViaPointer(arrayOfShapes[i]);216217

//LoopthrougharrayOfShapesandcallvirtualViaReference218

//toprinttheshapename,attributes,area,andvolume219

//ofeachobjectusingdynamicbinding.220cout<<"Virtualfunctioncallsmadeoff"221<<"base-classreferences\n";2223.FunctionDefinitions223

for(intj=0;j<3;j++)224virtualViaReference(*arrayOfShapes[j]);225226

return

0;227}//endfunctionmain228229//Makevirtualfunctioncallsoffabase-classpointer230//usingdynamicbinding.

231voidvirtualViaPointer(constShape*baseClassPtr)

232{

233baseClassPtr->printShapeName();

234baseClassPtr->print();

235cout<<

"\nArea="<<baseClassPtr->area()236<<"\nVolume="<<baseClassPtr->volume()<<"\n\n";237}//endfunctionvirtualViaPointer

3.FunctionDefinitions238239//Makevirtualfunctioncallsoffabase-classreference240//usingdynamicbinding.241voidvirtualViaReference(constShape&baseClassRef)242{243baseClassRef.printShapeName();244baseClassRef.print();245cout<<"\nArea="<<baseClassRef.area()246<<"\nVolume="<<baseClassRef.volume()<<"\n\n";247}

//endfunctionvirtualViaReferenceProgramOutputPoint:[7,11]Circle:[22,8];Radius=3.50Cylinder:[10,10];Radius=3.30;Height=10.00

Virtualfunctioncallsmadeoffbase-classpointersPoint:[7,11]Area=0.00Volume=0.00

Circle:[22,8];Radius=3.50Area=38.48Volume=0.00

Cylinder:[10,10];Radius=3.30;Height=10.00Area=275.77Volume=342.12

Virtualfunctioncallsmadeoffbase-classreferencesPoint:[7,11]Area=0.00Volume=0.00

Circle:[22,8];Radius=3.50Area=38.48Volume=0.00

Cylinder:[10,10];Radius=3.30;Height=10.00Area=275.77Volume=342.12Override,OverloadandHideOverride(重写),是指派生类重写基类的虚函数。重写的虚函数必须有一致的参数表和返回值(注意特殊情况)。Overload(重载),是指编写一个与已有函数同名但是参数表不同的函数。例如一个函数即可以接受整型数作为参数,也可以接受浮点数作为参数。

Hide(隐藏或遮蔽),是指派生类中的函数屏蔽了基类中相同名字的函数。遮蔽可以理解为特殊的覆盖和特殊的重载,分为两种情况:派生类的函数与基类的函数同名,但参数表不同。

(特殊的重载)派生类的函数与基类的函数同名且参数表相同,但没有加上virtual关键字。

(特殊的重写)7.9Polymorphism,

virtualFunctionsandDynamicBinding“UndertheHood”在面向对象的编程中,首先会针对数据进行抽象(确定基类)和继承(确定派生类),构成类层次。这个类层次的使用者在使用它们的时候,如果仍然在需要基类的时候写针对基类的代码,在需要派生类的时候写针对派生类的代码,就等于类层次完全暴露在使用者面前。如果这个类层次有任何的改变(增加了新类),都需要使用者“知道”(针对新类写代码)。这样就增加了类层次与其使用者之间的耦合。7.9Polymorphism,

virtualFunctionsandDynamicBinding“UndertheHood”多态可以使程序员脱离这种窘境。再回头看看下面的例子感受一下。假设我们实现一个有100种动物的动物园系统。由于其中不同种类的动物有着不同的eat和sleep行为,所以不能把eat和sleep提取出到基类Animal中。如何让所有种类的动物eat和sleep呢?不用多态不用多态,函数调用只能在编译时绑定。这就我们需要自己指定每一种动物,然后调用它自己的eat和sleep函数。可以目测,这会严重降低效率,同时使工作变得异常枯燥。动物们的吃饭的代码如下:

dog.eat(); donkey.eat(); chichen.eat();parrot.eat();horse.eat();monkey.eat();donkey.eat();mule.eat();pig.eat();cow.eat();deer.eat();ox.eat();goat.eat();lion.eat();tiger.eat();fox.eat();wolf.eat();bear.eat();…

动物们的睡觉的代码如下:

dog.sleep(); donkey.sleep(); chichen.sleep();parrot.sleep();horse.sleep();monkey.sleep();donkey.sleep();mule.sleep();pig.sleep();cow.sleep();deer.sleep();ox.sleep();goat.sleep();lion.sleep();tiger.sleep();fox.sleep();wolf.sleep();bear.sleep();…

创建对象的代码如下:

Dogdog; Donkeydonkey; Chichenchichen;Parrotparrot;Horsehorse;Monkeymonkey;Donkeydonkey;Mulemule;Pigpig;Cowcow;Deerdeer;Oxox;Goatgoat;Lionlion;Tigertiger;Foxfox;Wolfwolf;Bearbear;…

使用多态创建对象的代码如下:

Animal*ptr[100];ptr[0]=newDog; ptr[1]=newDonkey;ptr[2]=newChichen;ptr[3]=newParrot;…ptr[98]=newHorse;ptr[99]=newMule;动物们的吃饭的代码如下:

for(inti=0;i<100;i++){ptr[i]->eat();}动物们睡觉的代码如下:for(inti=0;i<100;i++){ptr[i]->sleep();}这完全归功于多态--编译器针对虚函数产生了可以在运行时刻确定被调用函数的代码。7.9Polymorphism,

virtualFunctionsandDynamicBinding“UndertheHood”WhentousePolymorphism在你设计一个基类的时候,如果发现一个函数需要在派生类里有不同的表现,那么它就应该是虚的。从设计的角度讲,出现在基类中的虚函数是接口,出现在派生类中的虚函数是接口的具体实现。通过这样的方法,就可以将对象的行为抽象化。7.9Polymorphism,

virtualFunctionsandDynamicBinding“UndertheHood”Compile-timeBinding(编译期绑定,alsocalledStaticBinding)和Run-timeBinding(运行期绑定,alsocalledDynamicBinding)。Binding(绑定):决定执行哪一段代码。

voidsayHi(){ cout<<“Hello!”<<endl;}

intmain(){ sayHi();

return0;}将sayHi的调用绑定到sayHi的实现处:编译器将所有对sayHi的调用视为执行代码:

cout<<“Hello!”<<endl;C++编译期绑定和运行期绑定一个函数的名称对应了该函数在内存中的起始地址,常称作该函数的入口地址。编译期绑定:执行哪一段代码由编译器在编译阶段决定;即函数的入口地址在编译时确定。运行期绑定:执行哪一段代码是在运行阶段决定;即函数的入口地址在运行时确定。1 classBase{2public:3 voidf(){cout<<“Base::f()”<<endl;}4};5

classDerive:publicBase{6

public:7

voidf(){cout<<“Derive::f()”<<endl;}9 };10

int

main(){

Base*ptr=NULL;

ptr=newDerive;

ptr->f(); deleteptr; return0;

}

Base::f()Output编译期绑定:在编译时,根据ptr的数据类型Base*进行绑定,即绑定到Base::f()。编译期绑定和运行期绑定的区别1 classBase{2public:3 virtualvoidf(){cout<<“Base::f()”<<endl;}4};5

classDerive:publicBase{6

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论