C++多态性与虚函数_第1页
C++多态性与虚函数_第2页
C++多态性与虚函数_第3页
C++多态性与虚函数_第4页
C++多态性与虚函数_第5页
已阅读5页,还剩85页未读 继续免费阅读

下载本文档

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

文档简介

第9章多态性与虚函数

多态性〔Polymorphism〕是面向对象程序设计的主要特征之一。多态性对于软件功能的扩展和软件重用都有重要的作用。是学习面向对象程序设计必须要掌握的主要内容之一。第十一章多态性9.1多态性的概念

9.2继承中的静态联编

9.3虚函数和运行时的多态

9.4纯虚函数和抽象类

***9.5模板9.1多态性的概念

面向对象程序设计中多态的表现

多态性是指同样的消息被不同类型的对象接收时导致完全不同的行为消息——指示要调用类的某个成员函数行为——成员函数执行的结果被视为对象的行为TruepolymorphismChoiceofwhichfunctiontoexecuteismadeduringruntimeC++usesvirtualfunctions多态性——例#include<iostream>usingnamespacestd;classStudent{public:floatTuition(){//计算学费cout<<“student”<<endl;return2500.0;}};classGraduate:publicStudent{public:

floatTuition(){cout<<“Graduate”<<endl;return8000.0}};voidmain(){Students;Graduategs;s.Tuition();//s的学费

gs.Tuition();//gs的学费}不同类的对象调用不同的成员函数面向对象程序设计中多态的表现面向对象程序设计中多态性表现为以下几种形式:重载多态:通过调用相同名字的函数,表现出不同的行为。运算符重载也是一种重载多态。运行多态:通过基类的指针,调用不同派生类的同名函数(虚函数),表现出不同的行为。许多面向对象程序设计的书籍中所说的多态性,就是这种多态。模板多态:也称为参数多态,通过一个模板,得到不同的函数或不同的类。这些函数或者类具有不同的特性和不同的行为。9.1.2多态的实现:联编

一个具有多态性的程序语句,在执行的时候,必须确定究竟是调用哪一个函数确定具有多态性的语句究竟调用哪个函数的过程称为联编〔Binding〕,有的资料也翻译成“关联”或者“绑定”9.1.2多态的实现:联编联编有两种方式:静态联编动态联编9.1.2静态联编在源程序编译的时候就能确定具有多态性的语句调用哪个函数,称为静态联编。对重载函数的调用就是在编译的时候确定具体调用哪个函数,所以是属于静态联编。9.1.2动态联编动态联编那么是在程序运行时,才能够确定具有多态性的语句究竟调用哪个函数。用动态联编实现的多态,也称为运行时的多态虚函数是支持运行时多态的根底9.2继承中的静态联编

9.2继承中的静态联编通过派生类对象调用同名成员函数通过基类指针调用同名成员函数1.通过派生类对象调用同名成员函数

在派生类中可以定义和基类的成员函数同名的成员函数。这是对基类进行改造,是派生类增加新的行为的一种常用的方法在程序编译的时候,就可以确定派生类对象具体调用哪个同名的成员函数。这是通过静态联编实现的多态

例9.1定义Circle类和Rectangle类为Shape类的派生类,通过Circle类和Rectangle类的对象调用重载函数getArea()显示对象的面积。

#ifndefSHAPE_H#defineSHAPE_HclassShape{public:doublegetArea();voidprint();};

classCircle:publicShape{ public:Circle(int=0,int=0,double=0.0);doublegetArea();//返回面积voidprint();private:intx,y;//圆心doubleradius;//半径};

classRectangle:publicShape{ public: Rectangle(int=0,int=0);

doublegetArea();//面积

voidprint();private: inta,b;//长和宽};#endif

//例9.1:shape.cpp#include<iostream>usingnamespacestd;#include"shape.h" doubleShape::getArea(){cout<<"基类的getArea函数,面积是";return0.0;} voidShape::print(){ cout<<"BaseclassObject"<<endl;}

Circle::Circle(intxValue,intyValue,doubleradiusValue){ x=xValue;y=yValue; radius=radiusValue;} doubleCircle::getArea(){cout<<"Circle类的getArea函数,面积是";

return3.14159*radius*radius;} voidCircle::print(){cout<<"centeris";cout<<"x="<<x<<"y="<<y;cout<<";radiusis"<<radius<<endl;}

Rectangle::Rectangle(intaValue,intbValue){ a=aValue;b=bValue;} doubleRectangle::getArea(){cout<<"Rectangle类的getArea函数,面积是";

returna*b;} voidRectangle::print(){cout<<"hightis"<<a;cout<<"widthis"<<b<<endl;}

//例9.1:9_1.cpp#include<iostream>usingnamespacestd;#include"shape.h"voidmain(){Circlec(22,8,3.5);Rectangler(10,10);

cout<<"调用的是";cout<<c.getArea()<<endl; //静态联编

cout<<"调用的是";cout<<r.getArea()<<endl;//静态联编} 调用的是Circle类的getarea函数,面积是38.4845

调用的是Ractangle类的getarea函数,面积是100

通过派生类对象调用同名成员函数通过派生类对象调用同名成员函数,可以有以下的结论:派生类对象可以直接调用本类中与基类成员函数同名的函数,不存在二义性;在编译时就能确定通过对象将调用哪个函数,属于静态联编。Number

aNum;DerNumber

aDerNum;aNum.Prime()aDerNum.Prime();2.通过基类指针调用同名成员函数

从继承的角度来看,派生类对象是基类对象的一个具体的特例。或者说,派生类对象是某一种特定类型的基类对象例如,Circle类是Shape类的公有继承,“圆”是“图形”的一种特例。或者说,圆是一种特定的图形,具有图形的根本特征。2.通过基类指针调用同名成员函数在关于基类对象和派生类对象的操作上,可以允许以下的操作:可以用派生类对象给基类对象赋值;

例:Circlec(22,8,3.5);Rectangler(10,10);Shapea=c;Shapeb=r;2.通过基类指针调用同名成员函数可以用派生类对象初始化基类的引用例Shape&sp=c;

可以用派生类对象的地址给基类指针赋值例:Shape*pc=&r;通过该指针,可以访问基类的公有成员。2.通过基类指针调用同名成员函数以下这些操作是不能进行的:不能用基类对象给派生类对象赋值;例Shapesp;

Circler=sp;不能用基类对象的地址初始化派生类对象的指针;

例Shapesp;Circle*pc=&sp;基类对象不能初始化派生类的引用;例Shapesp;Circle&pc=sp;

例9.2在例9.1所定义的类的根底上,观察通过派生类对象地址初始化的基类对象的指针访问getArea函数的结果。#include<iostream>usingnamespacestd;#include"shape.h" voidmain(){Shape*shape_ptr; Circlecircle(22,8,3.5); Rectanglerectangle(10,10); shape_ptr=&circle;//Circle类对象地址初始化基类指针cout<<"circle对象初始化shape_ptr指针访问的getArea函数是"<<endl;cout<<shape_ptr->getArea()<<endl;//静态联编

shape_ptr=&rectangle;//Rectangle类对象地址初始化基类指针cout<<"rectangle对象初始化shape_ptr指针访问的getArea函数是"<<endl;cout<<shape_ptr->getArea()<<endl;//静态联编} circle对象初始化shape_ptr指针访问的getArea函数是基类的getArea函数,面积是0rectangle对象初始化shape_ptr指针访问的getArea函数是基类的getArea函数,面积是0通过基类指针调用同名函数的说明

程序运行结果说明:确实可以用派生类对象的地址初始化基类对象的指针;通过用派生类对象地址初始化的基类对象指针,只能调用基类的公有成员函数。在以上例子中,就是调用基类的getArea函数,而不是派生类的getArea函数;这种调用关系确实定,也是在编译的过程中完成的,属于静态联编。9.3虚函数和运行时的多态9.3虚函数和运行时的多态虚函数能够实现运行时的多态9.3.1虚函数在类的定义中声明虚函数,格式如下:virtual返回值类型函数名(参数表);在函数原型中声明函数是虚函数后,具体定义这个函数时就不需要再说明它是虚函数了

在基类定义中直接定义虚函数的格式是:virtual返回值类型函数名(参数表){//函数体}

9.3.1虚函数基类中的同名函数声明或定义为虚函数后,派生类的同名函数无论是不是用virtual来说明,都将自动地成为虚函数从程序可读性考虑,一般都会在这些函数的声明或定义时,用virtual来加以说明

例9.3将例9.2进行修改,使得程序具有运行时的多态的效果。

//例9.3:shape1.h#ifndefSHAPE_H#defineSHAPE_HclassShape{ public:

virtualdoublegetArea(); voidprint(); };

classCircle:publicShape{ public: Circle(int=0,int=0,double=0.0);

virtualdoublegetArea();

voidprint();private: intx,y;

doubleradius; };

classRectangle:publicShape{ public: Rectangle(int=0,int=0);

virtual

doublegetArea();

voidprint();private: inta,b;};#endif//例9.3:shape1.cpp#include<iostream>usingnamespacestd;#include"shape1.h" doubleShape::getArea(){cout<<"基类的getArea函数,面积是";return0.0;}voidShape::print(){ cout<<"BaseclassObject"<<endl;}

Circle::Circle(intxValue,intyValue,doubleradiusValue){ x=xValue;y=yValue; radius=radiusValue;} doubleCircle::getArea(){cout<<"Circle类的getArea函数,面积是";

return3.14159*radius*radius;} voidCircle::print(){cout<<"centeris";cout<<"x="<<x<<"y="<<y;cout<<";radiusis"<<radius<<endl;} Rectangle::Rectangle(intaValue,intbValue){a=aValue;b=bValue;} doubleRectangle::getArea(){cout<<"Rectangle类的getArea函数,面积是";

returna*b;} voidRectangle::print(){cout<<"hightis"<<a;cout<<"widthis"<<b<<endl;}

//例9.3:9_3.cpp#include<iostream>usingnamespacestd;#include"shape1.h" voidmain(){Shape*shape_ptr;

Circlecircle(22,8,3.5);Rectanglerectangle(10,10);shape_ptr=&circle; cout<<“circle对象初始化shape_ptr指针访问的getArea函数是”<<endl;cout<<shape_ptr->getArea()<<endl;//动态联编

shape_ptr=&rectangle;

cout<<"rectangle对象初始化shape_ptr指针访问的getArea函数是"<<endl;cout<<shape_ptr->getArea()<<endl;//动态联编}

circle对象初始化shape_ptr指针访问的getArea函数是Circle类的getArea函数,面积是38.4845rectangle对象初始化shape_ptr指针访问的getArea函数是Rectangle类的getArea函数,面积是100动态联编:一种运行时决定的多态性9.3.1虚函数要实现运行时的多态,需要以下条件:通过指向基类对象的指针访问和基类成员函数同名的派生类成员函数;shape_ptr=&circle; shape_ptr->getArea();

或者用派生类对象初始化的基类对象的引用访问和基类成员函数同名的派生类成员函数;shape&sp=circle; sp.getArea();

派生类的继承方式必须是公有继承;基类中的同名成员函数必须定义为虚函数。

9.3.2虚函数的使用虚函数必须正确的定义和使用。否那么,即使在函数原型前加了virtual的说明,也可能得不到运行时多态的特性。必须首先在基类中声明虚函数。在多级继承的情况下,也可以不在最高层的基类中声明虚函数。例如:在第二层定义的虚函数,可以和第三层的虚函数形成动态联编。9.3.2虚函数的使用基类和派生类的同名函数,必须函数名、返回值、参数表全部相同,才能作为虚函数来使用。否那么,即使函数用virtual来说明,也不具有虚函数的行为;静态成员函数不可以声明为虚函数。构造函数也不可以声明为虚函数;析构函数可以声明为虚函数,即可以定义虚析构函数。

例9.4分析以下程序,编译时哪个语句会出现错误?为什么?将有错误的语句屏蔽掉以后,程序运行结果如何?其中哪些调用是静态联编,哪些是动态联编?

#include<iostream.h>classB{public:virtualvoidvf1(){cout<<"B::vf1被调用\n";}virtual

voidvf2(){cout<<"B::vf2被调用\n";}voidf(){cout<<"B::f被调用\n";}};classD:publicB{public:virtualvoidvf1(){cout<<"D::vf1被调用\n";}voidvf2(inti){cout<<i<<endl;}voidf(){cout<<“D::f\n被调用”;}};

voidmain(){Dd;B*bp=&d;bp->vf1();bp->vf2();bp->vf2(10);}将这个语句注释掉后,运行结果将显示:

D::vf1被调用B::vf2被调用函数调用bp->vf2(10);是错误的。因为派生类的vf2函数和基类的vf2函数的参数不同,派生类的vf2就不是虚函数。

其中bp->vf1()调用是动态联编。bp->vf2()是静态联编。9.3.3虚析构函数如果用动态创立的派生类对象的地址初始化基类的指针,创立的过程不会有问题:仍然是先调用基类构造函数,再执行派生类构造函数但是,在用delete运算符删除这个指针的时候,由于指针是指向基类的,通过静态联编,只会调用基类的析构函数,释放基类成员所占用的空间。而派生类成员所占用的空间将不会被释放

#include<iostream>usingnamespacestd;classShape {public:Shape(){cout<<"Shape类构造函数被调用\n";}~Shape(){cout<<"Shape类析构函数被调用\n";}; }; classCircle:publicShape {public:Circle(intxx=0,intyy=0,doublerr=0.0){x=xx;y=yy;radius=rr;cout<<"Circle类构造函数被调用\n";}~Circle(){cout<<"Circle类析构函数被调用\n";}private:intx,y; doubleradius; }; voidmain(){Shape*shape_ptr;shape_ptr=newCircle(3,4,5);deleteshape_ptr;}定义简单的Shape类和Circle类,观察基类指针的创立和释放时如何调用构造函数和析构函数程序运行后在屏幕上显示:Shape类构造函数被调用Circle类构造函数被调用Shape类析构函数被调用9.3.3虚析构函数为了解决派生类对象释放不彻底的问题,必须将基类的析构函数定义为虚析构函数。格式:

virtual

Shape();

此后,无论派生类析构函数是不是用virtual来说明,也都是虚析构函数。再用deleteshape_ptr来释放基类指针时,就会通过动态联编调用派生类的析构函数。9.4纯虚函数和抽象类9.4纯虚函数和抽象类前例,基类Shape并不是一个具体的“形状”的抽象,而是各种实际的“形状”的抽象在C++中,对于那些在基类中不需要定义具体的行为的函数,可以定义为纯虚函数

对于那些只是反映一类事物公共特性的类,在C++中可以定义为“抽象类”

9.4纯虚函数和抽象类纯虚函数的声明格式:

virtual返回值类型函数名(参数表)=0;9.4纯虚函数和抽象类纯虚函数的声明和使用有以下的特点:纯虚函数一定是在基类中声明的;在多级继承的情况下,纯虚函数除了在最高层基类中声明外,也可以在较低层的基类中声明;纯虚函数是没有函数体的。函数体是用“=0”来代替了;纯虚函数是不可以被调用的。但凡需要被调用的函数都不可以声明为纯虚函数。9.4纯虚函数和抽象类包含纯虚函数的类为抽象类

抽象类定义的一般形式是:class类名{public:

virtual

返回值类型函数名(参数表)=0;//其他函数的声明;};9.4纯虚函数和抽象类抽象类的定义和使用具有以下的特点:

不可以定义抽象类的对象;可以定义抽象类的指针和抽象类的引用。目的是通过这些指针或引用访问派生类的虚函数,实现运行时的多态;如果抽象类的派生类中没有具体实现纯虚函数的功能,这样的派生类仍然是抽象类;抽象类中除了纯虚函数外,还可以定义其他的非纯虚函数。

例9.6编写一个程序,可以创立Circle类或者Rectangle类的对象,并且显示所创立对象的面积。在编程中注意使用多态性。

//例9.6:shape2.hclassShape{ //基类Shape的定义public:

virtualdoublegetArea()=0;//纯虚函数

voidprint();

virtual~Shape(){} //虚析构函数};

classCircle:publicShape{public: Circle(int=0,int=0,double=0.0); doublegetArea();//返回面积

voidprint();//输出Circle类对象tprivate: intx,y; //圆心座标

doubleradius;//圆半径};

classRectangle:publicShape{ public: Rectangle(int=0,int=0); //构造函数

doublegetArea();//返回面积

voidprint();//输出Rectangle类对象private: inta,b;//矩形的长和宽};

//例9.6:shape2.cpp#include<iostream>usingnamespacestd;#include"shape2.h"

voidShape::print(){ cout<<"BaseclassObject"<<endl;} Circle::Circle(intxValue,intyValue,doubleradiusValue){x=xValue;y=yValue;radius=radiusValue;}

doubleCircle::getArea(){cout<<"Circle类的getArea函数,面积是";

return3.14159*radius*radius;} voidCircle::print(){cout<<"centeris";cout<<"x="<<x<<"y="<<y;cout<<";radiusis"<<radius<<endl;}

Rectangle::Rectangle(intaValue,intbValue){ a=aValue;b=bValue;} doubleRectangle::getArea(){cout<<"Rectangle类的getArea函数,面积是";

returna*b;} voidRectangle::print(){cout<<"hightis"<<a;cout<<"widthis"<<b<<endl;}

//例9.6:9_6.cpp#include<iostream>usingnamespacestd;#include"shape2.h" voidmain(){Circlec(1,2,3);Rectangler(1,2);Shape*shape_ptr;shape_ptr=&c;//指针指向circle类对象cout<<shape_ptr->getArea()<<endl;//动态联编shape_ptr=&r;//指针指向rectangle类对象cout<<shape_ptr->getArea()<<endl;//动态联编

} Circle类的getArea函数,面积是28.27Rectangle类的getArea函数,面积是2说明这个程序中,使用了本章所介绍的虚函数、纯虚函数、虚析构函数、基类指针访问派生类对象等技术,实现了运行时的多态;程序具有很好的可扩展性。如果需要增加新的派生类,当然要增加和派生类定义有关的代码,和创立对象所需要的语句;抽象类中可以为各派生类定义一些通用的接口。这些通用的接口就是抽象类中的纯虚函数。新增加的派生类的对象,都可以使用这样的通用接口,表现派生类对象的行为特性。9.5模板9.5模板函数重载

intmax(intx,inty);floatmax(floatx,floaty);模板是C++中的通用程序模块。在这些程序模块中有一些数据类型是不具体的,或者说是抽象的。当这些抽象的数据类型更换为不同的具体数据类型以后,就会产生一系列具体的程序模块

template<typenameT>Tmax(Tx,Ty);考虑只写一个函数如何?9.5模板C++中的模板包括函数模板类模板函数模板实例化后产生的函数,称为模板函数类模板实例化后产生的类,称为模板类

9.5.1函数模板函数模板定义的根本格式:template<typename参数化类型名>函数返回类型函数名(形式参数列表){函数体}“template”是定义模板的关键字;在一对尖括号<>内,关键字“typename”后面声明所使用的“参数化类型名”;关键字“typename”可以用“class”取代。9.5.1函数模板模板的其余局部和一般的函数定义的格式完全相同。只是在函数定义时可以使用参数化类型来代表各种具体的数据类型。参数化类型可以用于:函数返回值类型;函数形式参数的类型;函数体内,自动变量的类型。

例9.9函数模板的定义和使用:定义和使用确定3个数据的最大值函数模板。#include<iostream>usingnamespacestd;template<typenameT>Tmax_value(Tx,Ty,Tz)//函数模板的定义:求x、y、z的最大值{Ttemp; temp=x>y?x:y; returntemp>z?temp:z;}voidmain(){cout<<max_value(12,32,21)<<endl;//用整数作实参调用函数模板cout<<max_value('a','A','9')<<endl;//用字符作实参调用函数模板}程序执行后,输出: 32

a

例9.10编写一个带有两个形式参数的函数模板,可以按指定的操作数类型相乘。

#include<iostream>

template<typenameP1,typenameP2>P1cal(P1x,P2y)//有两个参数化类型名:P1和P2{return(x*(P1)y);}//按x类型进行乘法voidmain(){intw=2;floatz=1.5;cout<<cal(w,z)<<endl;//按整型数相乘

cout<<cal(z,w)<<endl;//按浮点数相乘}程序运行的结果是:23.0带有确定类型的参数的函数模板例:template<classQ1>

voidArrayInput(Q1array,intnum){//输入数组元素//……

}函数模板的形式参数表中除了使用参数化类型名以外,还可以使用确定类型的参数。也就是说,函数模板的参数表中,一定要包含参数化类型名,但不一定都使用参数化类型名。还可以根据需要,使用确定类型的参数。

例9.11设计和编写一个通用的输入数组元素的函数模板。可以用它来输入各种不同数据类型的数组。

#include<iostream.h>#include<typeinfo.h>template<classQ1> //函数模板

voidArrayInput(Q1array,intnum){cout<<"输入"<<num<<"个"<<typeid(Q1).name()<<'\b'<<"型数据"<<endl;for(unsignedj=0;j<num;j++)//输入数组元素 cin>>array[j]; }typeid运算符,它可以在程序运行时,显示指定的数据的类型voidmain(){intnumber; floatfloatArray[4];intintArray[3]; number=sizeof(floatArray)/sizeof(float);ArrayInput(floatArray,number);//输入整型数组元素

number=sizeof(intArray)/sizeof(int);ArrayInput(intArray,number);//输入浮点型数组元素}9.5.2函数模板使用中的问题

用户定义的类取代参数化类型:在这种情况下,函数模板实例化后,在函数的表达式中参与运算的就是类的对象。对象可以直接使用的运算符只有赋值运算符“=”。如果表达式中需要对象进行其他的运算,就必须在相应的类的定义中,对于要使用的运算符进行重载。例如,在例9.9中,函数模板是求3个实参的最大值,需要使用运算符“>”。如果要用对象来作实参,相应的类中要对“>”运算符进行重载。

例9.12用例9.9的函数模板,求3个Circle类对象的最大值。

#include<iostream> template<typenameT>Tmax_value(Tx,Ty,Tz)//函数模板的定义

{Ttemp; if(x>y)temp=x; elsetemp=y; if(z>temp)temp=z; returntemp;}

classCircle {private: intx,y; //圆心座标

doubleradius; //圆半径public:Circle(inta=0,intb=0,doublec=0.0) {x=a;y=b;radius=c;}friendostream&operator<<(ostream&,Circle&);//重载“<<”运算符 intoperator>(Circlem2)//重载“>”运算符

{if(radius>m2.radius) return1; elsereturn0; }};

ostream&operator<<(ostream&out,Circle&C1){out<<"x="<<C1.x<<"y="<<C1.y;out<<"radius="<<C1.radius;returnout;}voidmain(){CircleC1(2,3,5),C2(3,5,8),C3(3,2,6);//定义3个Circle类对象

cout<<max_value(12,32,21)<<endl;//用整数作实参

cout<<max_value(‘a’,‘A’,‘9’)<<endl;//用字符作实参cout<<max_value(C1,C2,C3)<<endl;//用对象作参数}程序运行结果是:32ax=3y=5radius=8

9.5.2函数模板使用中的问题函数模板不支持参数自动转换

对于例9.9的函数模板:

template<typenameT> Tmax_value(Tx,Ty,Tz);如果用以下的方式来调用: cout<<max_value(12,3.2,21)<<endl;在编译时会出现编译错误:“templateparameter'T'isambiguous”要解决这样的问题,就需要函数模板和一般的函数联合使用。即需要函数模板和一般的函数的重载

9.5.3重载函数模板重载函数模板可以是函数模板和另一个参数数目不同的函数模板的重载,也可以是函数模板和非模板函数的重载。一般所说的函数模板重载是指后一种情况。函数模板和非模板函数重载,可以解决函数模板不支持参数自动转换的问题;当所使用的参数类型〔不是类类型〕不支持函数模板中某些操作时,就需要为这样的参数专门编写相应的非模板函数。和已经定义的函数模板形成重载的关系。9.5.3重载函数模板例如,需要用例9.9的函数模板来比较3个结构体变量,返回其中的最大值。由于结构体变量不支持直接的比较操作“>”。要实现这样的功能,就要再写一个函数。假定结构体的定义是: structcourse {char*name; floatscore; };9.5.3重载函数模板三个结构体变量的比较函数如下:coursemax_value(courses1,courses2,courses3){courses4;if(s1.score>s2.score)

温馨提示

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

评论

0/150

提交评论