




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
绪论面向对象程序设计:通过增加软件可扩充性和可重用性来改善并提高程序员的生产能力,并控制维护软件的复杂性和软件维护的开销OOP:具有结构化程序设计的一切优点
将数据和对数据的操作放在一起,作为一个相互依存、不可分割的一个整体,抽象成一种新的数据类型—类两大技术(两个重要原理)数据抽象信息隐藏OPP支持的软件开发策略
编写可重用代码编写可维护的代码共享代码精化已有的代码
OOP的五大基本概念1.对象(Object):数据及可以对这些数据施加的操作结合在一起所构成的独立实体的总称,是计算的最基础的构造块成员变量(数据):表明对象的状态成员方法(操作):表明对象的行为一组成员变量和相关的方法集合2.类(Class):对一组具有相同数据和相同操作的对象的描述(定义),依据共同的行为将有关对象进行的一种分组类中所有对象共享类中所有共同的特性类是对象的抽象程序中:只有类对象是类的实例运行时:只有对象4.消息(Message):要求某个对象执行类中所定义的某个操作的规格说明
5.方法(Method):对象所能执行的操作
它是类中定义的函数,描述对象执行操作的算法,即响应消息的方法OOP方法学的四大要点1.认为世界由各种对象(object)组成,任何事物都是对象,是某个对象类(class)的实例(instance)2.把所有对象都划分成各种对象类,每个对象类都定义了一组方法(method),即允许施加于该类对象上的各种操作OOP的三大核心特征
封装性(Encapsulation)继承性(Inheritance)多态性(Polymorphism)1.封装性:数据和加工处理该数据的方法紧密结合在一起构成黑匣子的整体对私有数据不能执行该对象的成员函数之外的任何其它函数2.继承性:一个类直接继承其父类的全部描述(数据和函数)继承具有传递性:若类C继承类B,类B继承类A,则类C继承类A类实际上继承了类等级中在其上层的全部基类(父类)的所有描述继承方式分类(二)单继承:一个类只有一个父类时(树结构)多继承:一个类可有多个父类时(图结构)3.多态性:在类等级的各层中共享(公用)一个行为(函数)的名字,而类等级中的每个类却各自按照自己的需要来实现这个行为一个名字,多种语义相同界面,多种实现函数重载表达了最简单的多态性voidf(int,int,char);voidf(char,float);voidf(int,int);voidf(float,float);
参数数量、类型、顺序不同,函数体也可以完全不同2.行为共享:(1)实体(模块)的外部接口称为行为(2)行为共享允许多个实体(模块)具有相同的接口集(3)行为共享增强系统的灵活性(4)行为共享增强系统的抽象
流流是一个抽象的概念,它指的是数据从一个源(source)到一个终点(sink)的流动c++提供四个预定义的开放流cin:标准输入,通常指键盘,对应于c中的stdin,cout:标准输出,通常指屏幕,对应于c中的stdoutcerr:标准错误输出,通常指屏幕,对应于c中的stderrclog:cerr的全缓冲版(c中没有等价的)cerr:无缓冲clog:有缓冲定义或说明一个常数组可采用如下格式:<类型说明符>const<数组名>[<大小>]…或者const<类型说明符><数组名>[<大小>]…intconsta[5]={1,2,3,4,5};常对象是指对象常量,定义格式如下:<类名>const<对象名>或者const<类名><对象名>定义常对象时,同样要进行初始化,并且该对象不能再被更新下面定义了一个指向字符串常量的指针:constchar*ptr2=stringprt1;其中,ptr2是一个指向字符串常量的指针。ptr2所指向的字符串不能更新的,而ptr2是可以更新的。因此,*ptr2="x";是非法的,而:ptr2=stringptr2;是合法的。(1)指向常量的指针:指向的常量不能改变1)constchar*pc=”asdf”;//指向常量的指针pc[3]=’a’;//错pc=”ghik”;//对2)constchar*step[3]={”left”,”right”,”top”};step[2]=”skip”;//对step[2][1]=’i’;//错(2)常指针:指针本身不能改变,指向的对象可以改变1)char*constpc=”asdf”;//常指针pc[3]=’a’;//对pc=”ghik”;//错2)char*conststep[3]={“left”,”right”,”top”};step[2]=”skip”;//错step[2][1]=’i’;//对(3)指向常量的常指针:指针本身及指向的对象均不能改变constchar*constpc=”asdf”;//指向常量的常指针pc[3]=’a’;//错pc=”ghik”;//错注意:(1)const总是修饰紧随其后的东西(2)指向常量的指针不能赋给一般的指针,一般的指针可以赋给指向常量的指针2.常引用使用const修饰符也可以说明引用,被说明的引用为常引用,该引用所引用的对象不能被更新。其定义格式如下:const<类型说明符>&<引用名>例如:constdouble&v;在实际应用中,常指针和常引用往往用来作函数的形参,这样的参数称为常参数。在C++面向对象的程序设计中,指针和引用使用得较多,其中使用const修饰的常指针和常引用用得更多。使用常参数则表明该函数不会更新某个参数所指向或所引用的对象,这样,在参数传递过程中就不需要执行拷贝初始化构造函数,这将会改善程序的运行效率。#includeconstintN=6;voidprint(constint*p,intn);voidmain(){intarray[N];for(inti=0;icin>>array[i];print(array,N);}voidprint(constint*p,intn){cout<<"{"<<*p;for(inti=1;icout<<","<<*(p+i);cout<<"}"<}常成员函数使用const关键字进行说明的成员函数,称为常成员函数。只有常成员函数才有资格操作常量或常对象,没有使用const关键字说明的成员函数不能用来操作常对象。常成员函数说明格式如下:<类型说明符><函数名>(<参数表>)const;其中,const是加在函数说明后面的类型修饰符,它是函数类型的一个组成部分,因此,在函数实现部分也要带const关键字。下面举一例子说明常成员函数的特征。#includeclassR{public:R(intr1,intr2){R1=r1;R2=r2;}voidprint();voidprint()const;private:intR1,R2;};voidR::print(){cout<}voidR::print()const{cout<}voidmain(){Ra(5,4);a.print();constRb(20,52);b.print();}
该例子的输出结果为:5,420;522.Inline内联函数
包含在类的说明中的成员函数称为内部函数
它告诉编译器一旦有可能就直接用该函数的代码取代对该函数的调用,以避免函数调用的开销class类名{成员变量;成员函数;}
即使不是在类说明中定义的函数,也可以说明为内部的(加inline)以空间换时间内联函数的限制
(1)内联函数不能包含任意静态数据(2)内联函数不能使用任何循环switch,goto语句(3)内联函数不能是递归的(4)内联函数中不能有数组说明(可放在外面说明)几个重要概念1.函数原型intget_word(char*,int,float);
把函数的函数名,返回类型及使用的参数的类型和个数提前告诉编译程序1)函数原型通常放在程序的顶部2)c++需要所有函数都有原型2.缺省参数intg(chara,intb,intc=0);
调用时若未给第三个参数,则缺省为0(1)所有取缺省值的参数都必须出现在不取缺省值的参数的右边(2)缺省值只需在第一次被提及的地方指定3.引用类型数据的一种类型(同指针类型很相似)c只能通过传值传递参数,不能通过传递引用传递参数c++不仅通过传值传递参数,也可通过传递引用传递参数(1)引用的概念
引用是给对象(变量)取一个别名,它引入了对象的一个reference在C中只能使用指针建立别名,不能使用引用建立别名在C++中不仅使用指针建立别名,也可使用引用建立别名
定义引用的关键字是type&,它的含义是“type类型的引用”,此引用与type类型对象或变量的地址相联系
例一inti;int&j=i;
创建了一个整型引用变量j,它是i的地址的别名,即j是i的同义词,它们表示同一个对象例二inti;int&j;错j=i;定义引用类型变量时就必须初始化而externint&i;//正确,不必给出初值
例三
inti;int&j=i;i=5;j=i+1;i,j的值均为6引用是对象本身,而不是副本,所以对对象或对引用的任何操作都会影响到它们共同的对象一个引用可看作一个特殊类型的指针引用与指针的差别(1)引用必须初始化(2)初始化后不能再作为其他对象的别名引用可以使用临时变量来进行初始化(1)用常量初始化引用(引用为—常量的别名)int&ir=1024;intT1=1024;int&ir=T1;(T1为临时变量)(2)用不同类型的对象初始化引用unsignedintui=20;int&ir=ui;
unsignedintui=20;intT2=int(ui);int&ir=T2;(T2为临时变量)(2)引用的用途
独立引用作为函数参数类型作为函数返回类型1)独立引用
a.独立引用的限制b.理解int*&p的含义(从右往左理解)2)作为函数参数传递
比较传值、传指针、传引用inta1(intn){return3*n;main()中调用语句}intx,i=4;voida2(int*n)x=a1(i);//x=12,i=4{*n=(*n)*3;a2(&i);//i=12}a3(i);//i=36voida3(int&n){n=3*n;}传值的缺点1、单向传递,结果无法返回2、对于大型对象,实参对形参的赋值耗时太多
3)作为函数返回类型
甚至可使返回值作为赋值号左侧的元素,即函数调用可以放在赋值语句的左端类的一般形式class类名{private: 私有段数据及函数;protected:保护段数据及函数;public:公有段数据及函数;};成员变量和成员函数1.成员变量成员变量的声明与普通变量的声明方式相同,但声明中不允许进行初始化赋值
允许不允许classstack classstack {int*lst;{int *lst;intmax_size;intmax_size;intnb_elements;intnb_elements=0;};};一般在声明时,应按成员变量类型的大小,从小至大声明,这样利于机器进行对准操作,从而提高时空利用率(3)在类的内部,可以定义其自身的引用或者指针
classstack{int*lst;intmax_size;intnb_elements;};
classstacklinker{stackmystack;stacklinker*right,*left;};2.成员函数栈的六种操作{建栈、判栈、入栈、出栈、读栈、毁栈}在类中应给出成员函数的原型classstack{public:voidcreate(int);intempty();voidpush(int);voidpop();inttop();voiderase();};
成员函数的具体实现可在类定义体内(为内联函数),也可在类定义体外(加上inline成为内联函数,不加inline成为一般函数)stack的完整实现如下:
#include<iostream.h>#include<string.h>constchar*msg[]={”stackisempty!\n”,”stackisoverflow!\n”};classstack{int*lst;intnb_elements;intmax_size;voiderr_msg(int);public:voidcreate(int);intempty();voidpush(int);voidpop();inttop();voiderase();};viodstack::err_msg(intcode){cout<<msg[code];}voidstack::create(intsize){max_size=size;lst=newint(size);nb_elements=0;}intstack::empty(){return(nb_elements==0);}voidstack::push(intelement){if(nb_elements==max_size)err_msg(1);elselst[++nb_elements]=element;}voidstack::pop(){if(nb_elements==0)err_msg(0); else--nb_elements;}intstack::top(){if(nb_elements==0)err_msg(0);elsereturn(lst[nb_elements]);}voidstack::erase(){deletelst;}main() {stacks;inti;s.create(100);for(i=0;i<=100;++i)s.push(i);for(i=0;i<=100;++i){cout<<s.top()<<’,’;s.pop();}s.erase();结果:stackisoverflow}99,98,……1,0,stackisempty类类型常量和const成员函数1.
135为int型常量2.35.7为double型常量’C’’f’为char型常量某些类类型存在常量如:有复数类complex,则2+3i为complex类的类型常量
不存在像基本类型常量那样形式的类类型常量,类类型的常量是以其他方式表达的2.在程序中对const声明的常量进行赋值是不允许的constcharblank=’’;blank=’\0’;error3.只有通过公有成员函数才能修改一个类的对象为了确保某个对象是常量,编译器必须有办法区别“安全”及“不安全”的成员函数conststacks;cout<<s.top()<<’\n’; //安全,不改变ss.push(3); //不安全,常量不可改类的设计者通过const来指明哪些成员函数是安全的classstack{public:inttop()const;}只有被说明为const的成员函数才能由const常量对象调用1)在类的说明体之内定义的const成员函数const应放在函数名和函数体之间2)在类的说明体之外定义的const成员函数必须在声明及定义处都使用constclassstackclassstack{inttop()const {inttop()const; //声明{}………..intstack::top()const//定义}{};…………};若某个函数修改了类中的某个成员变量,则不允许把它声明为const型的classexample{int*data_member;public:voidok(inti)const //允许{*data-member=i;}voiderr(int*i)const //不允许{data-member=i;}};const成员函数也可以重载构造函数的功能:初始化对象析构函数的功能:释放空间1.构造函数:
系统缺省版本:做公共初始化工作用户提供版本:做用户初始化工作2.析构函数:
系统缺省版本:做公共的善后工作用户提供版本:做用户的善后工作
构造函数和析构函数都具有普通成员函数的许多共同特性,但也有独特的特性1.共同点
(1)没有返回类型 (2)不能被继承(3)不能取它们的地址
2.不同点
(1)构造函数可以有参数 析构函数不能有参数 (2)构造函数不能是虚函数析构函数可以是虚函数(3)构造函数不能显式调用析构函数可以显式调用当定义对象时系统自动调用构造函数(先系后用)当删除对象时系统自动调用析构函数(先用后系)(1)构造函数的名字必须与类名相同(2)构造函数没有返回值,在声明和定义构造函数时是不能说明类型的(3)构造函数只能对数据成员做初始化,这些数据成员一般为私有成员(4)构造函数一般不做赋初值以外的事情(5)构造函数不能显式地调用
析构函数类的一个特殊成员函数,函数名与类名相同,只是在前面加上一个“~”classstringstring::~string(){intlength;{deletecontents;char*contents; }public;string(char*);~string();};(1)一般析构函数由一系列的delete语句组成(2)若使用完全限定名,可显式调用析构函数参数化的构造函数classpoint{intx,y;public:point(int,int);voidoffset(int,int);};point::point(intvx,intvy){x=vx;y=vy;}voidpoint::offset(intax,intay){x=x+ax;y=y+ay;main()}{pointpt(10,20);pt.offset(10,10);}缺省参数的构造函数例一.classset{intelems[100];intsize;intcard;public:set(inti=16);};set::set(inti){size=i;if(size>100)size=100;if(size<16)size=16;main()}{sets1(64),s2(16),s3(8),s4;}size取值64161616例二.classpoint{intx,y;public:point(intvx=0;intvy=0){x=vx;y=vy;}};main(){pointp1; //不传递参数x=0,y=0pointp2(10); //只传递一个参数x=10,y=0pointp3(10,20);//传递两个参数x=10,y=20}重载构造函数
classx{public:x();x(int);x(int,char);x(float,char);};voidf(){xa,b(1),c(1,’c’),d(2.3,’d’);}注意二义性classx{public:x();x(inti=0);};main(){xa(10);//正确xb;//错误}拷贝构造函数
一种特殊的构造函数(1)为一个构造函数,当创建一个新对象时系统自动调用它(2)它将参数代表的对象逐域拷贝到新创建的对象中
拷贝构造函数是将自己所在的类的引用作为构造函数的参数拷贝构造函数的定义的两种形式1.系统产生2.用户定义1.系统产生#include<iostream.h>classpoint{intx,y;public:point(intvx,intvy){x=vx;y=vy;}voidprint(){cout<<x<<””<<y<<”\n”;}};main(){pointp1(10,20),p2(p1);结果:1020p1.print();p2.print();1020}2.用户定义#include<iostream.h>classpoint{intx,y;public:point(intvx,intvy){x=vx;y=vy;}point(constpoint&p){x=p.x;y=p.y;}voidprint(){cout<<x<<””<<y<<”\n”;}};main(){pointp1(10,20),p2(p1);结果:1020p1.print();p2.print();1020}也可采用赋值形式调用拷贝构造函数main()main(){pointp1(10,20);{pointp1(10,20);pointp2(p1);pointp2=p1;}}第三节this指针C++为成员函数提供了一个称为this的指针,this在所有成员函数调用里作为一个隐含参数传给成员函数classpoint{intx,y;public:point(intvx,intvy,point*constthis)//完整{this->x=vx;this->y=vy;}point(intvx,intvy)//半省{this->x=vx;this->y=vy;}point(intvx,intvy){x=vx;y=vy;}//全省};1.每个对象的成员函数都拥有一个隐含的指针this(静态函数除外)intget_length(){returnlength;}二者等价intget_length(){returnthis->length;}2.this指针是一个常指针,可表示为:x*constthis(可指向常量)3.this指针不能被修改和赋值4.const成员函数不能修改this所指的对象的成员第四节
静态成员一、静态成员变量(1)某个类的所有对象要共同访问同一个变元,这个变元可能是一个条件标志或一个计数器(2)为了减少数据冗余,防止不一致性,要求一个类的所有对象共享同一个数据(3)为了节约空间开销,也要求对象共享同一个数据处理方法:使用全局变量缺点:(1)破坏了信息隐藏原则(2)过多的全局变量会产生重名冲突
为了既达到与全局变量一样的效果,又能满足信息隐藏的原则,提出了静态成员变量的概念
特点
若类中某个成员变量被说明成静态的,则类的所有对象共享这个成员变量静态成员变量不属于某个对象,而属于整个类classABCD{charch;staticints;};ABCDa,b,c,d;
为了把某个成员变量说明为静态的,只须在原有说明前加上static静态成员变量可以是public、private和protected段的#include<iostream.h>#include<stdlib.h>classcounter{staticintcount;intobjnumber;public:counter(){count++;obinumber=count;}voidshow(){cout<<”obj”<<objnumber<<””cout<<”count=”<<count<<”\n”;}};intcounter::count=0;main(){counterobj1,obj2,obj3,obj4;obj1.show();cout<<”--------------------\n”;obj1.show();obj2.show();cout<<”--------------------\n”;obj1.show();obj2.show();obj3.show();cout<<”--------------------\n”;obj1.show();obj2.show();obj3.show();obj4.show();}输出结果:obj1 count=4----------------obj1 count=4obj2 count=4----------------obj1 count=4obj2 count=4obj3 count=4----------------obj1 count=4obj2 count=4obj3 count=4obj4 count=4静态成员函数static函数1.是一个成员函数,使用时要用“类名::”作为它的限定词2.是一个特殊的成员函数,它不属于某一个特定的对象而只属于一个类,不能象一般的成员函数那样随意地访问对象中的非静态的数据内容3.没有this指针4.要访问非静态成员,必须要有限定词#include<iostream.h>#include<string.h>classstring{staticinttotal_length;intlength;char*contents;public:string(char&s){length=strlen(s);contents=newchar[length+1];strcpy(contents,s);}staticintset_total_length(){total_length=total_length+length;//errorreturntotal_length;}~string(){delete[]contents;}};intstring::total_length=0;main(){stringobj1(“Thefirstobject”);cout<<string::set_total_length()<<”\n”;stringobj2(“Thesecondobject”);cout<<string::set_total_length()<<”\n”;}
要想让静态成员函数识别对象,只能靠参数传递的方式实现staticintset_total_length(stringobj){total_length=total_length+obj.length;returntotal_length;}main(){stringobj1(“Thefirstobject”);cout<<string::set_total_length(obj1)<<”\n”;stringobj2(“Thesecondobject”);cout<<string::set_total_length(obj2)<<”\n”;}在被调用静态成员函数前,必须缀上类名第五节
友元一、友元的概念(1)封装性和信息隐藏使对象和外界以一堵不透明的墙隔开,友元在这堵墙上开了一个小孔(2)友元是以牺牲信息隐藏原则,削弱封装性来提高编程效率,增大了类与类之间的藕合度只要将外界的某个对象说明为某一个类的友元,则它可以访问这个类中的所有成员
友元的种类1.一般函数2.另一个类的成员函数3.另一个类二、友元的声明和定义
将friend放在函数名或类名的前面,此声明可放在任何段中特别注意友元不属于任何类三、友元函数作为友元的函数classx{inti;friendvoidfunc(x*,int);//友元函数public:voidmember_func(int); //成员函数};voidfunc(x*xp,inta){xp→i=a;}//无需作用域voidx::member_func(inta){i=a;}//必须作用域成员函数可以在类中实现,也可以类外实现友元函数可以在类中实现,也可以类外实现在类外实现时,成员函数必须用作用域区分符(::)限定它属于哪一个类在类外实现时,友元函数不须用作用域区分符(::)限定它属于哪一个类友元函数没有this指针,不能确定是访问哪个对象的私有数据,故必须在参数表中显式地指明要访问的对象友元成员函数作为友元的另一个类中的成员函数classx{public:voidmember_func();};classy{inti;
friendvoidx::member_func();};友元类作为友元的另一个类
classx{inti,j;public:voidmember_func1();voidmember_func2();voidmember_func3();};classy{inti,j;friendx;};运算符重载
不能改变优先级不能改变操作数的个数不能重载没有的符号(自己发明)单目可以重载双目可以重载三目不能重载运算符重载的能力增强了C++语言的可扩充性new可以重载delete可以重载
在C++中,运算符的重载,实际上是一种函数调用的形式
用成员函数重载运算符用友元函数重载运算符+可以定义为减运算*可以定义为除运算
颠倒黑白重载方式的选择
(1)若运算符的操作要修改对象的状态,或需要左值运算数(如=,*=,++),选择成员函数(2)若运算符的操作数(特别是第一个操作数)希望有隐藏类型转换,必须选择友元函数既要声明、也要定义
用成员函数重载运算符重载运算符的成员函数称为运算符重载函数,有this指针,是一种特殊的成员函数1.声明格式classclass_name{typeoperator@(arg-list);}2.定义格式typeclass_name::operator@(arg-list){……}其中:type为返回类型,@为要重载的运算符operator为关键字,arg-list为该运算所需要的操作数若@为一元的,则arg-list为空,当前对象作为单操作数若@为二元的,则arg-list中有一个操作数,当前对象为@的左操作数,arg-list中的操作数为@的右操作数
隐式调用显式调用一元
aa@或@aaaa.operator@()
无参数,隐式传递
隐式调用显式调用二元
aa@bbaa.operator@(bb)
一个参数,一隐一显例1classx{intoperator+(x);};intx::operator+(xa){……}main(){xa1,b1;ya2,b2;a1=a1+b1;//正确a2=a2+b2;//错误}类x对+已进行重载类y对+未进行重载例2classpoint{intx,y;public:point(intvx,intvy){x=vx;y=vy;}point(){x=0;y=0;}pointoperator+(pointp);pointoperator-(pointp);voidprint(){cout<<x<<””<<y<<”\n”;}};pointpoint::operator+(pointp){pointpp;pp.x=x+p.x;pp.y=y+p.y;returnpp;}pointpoint::operator-(pointp){pointpp;pp.x=x-p.x;pp.y=y-p.y;returnpp;}main(){pointp1(10,10);p2(20,20);p1=p1+p2;p1.print();}
运算结果:3030用友元函数重载运算符重载运算符的友元函数也称为运算符重载函数1.声明格式classclass_name{friendtypeoperator@(arg-list);}2.定义格式typeoperator@(arg-list){……}若@为一元的,则arg-list中有一个操作数,作为唯一的操作数若@为二元的,则arg-list中有二个操作数,作为运算的两个操作数隐式调用显式调用一元aa@或@aaoperator@(aa)
一个参数,显式传递隐式调用显式调用二元aa@bboperator@(aa,bb)
二个参数,显式传递例1
classpoint{intx,y;public:point(intvx,intvy){x=vx;y=vy;}point(){x=0;y=0;}friondpointoperator+(pointp1;pointp2);friondpointoperator-(pointp1;pointp2);voidpoint(){cout<<x<<””<<y<<”\n”;}};pointoperator+(pointp1;pointp2);{pointp;p.x=p1.x+p2.x;p.y=p1.y+p2.y;returnp;}pointoperator-(pointp1;pointp2);{pointp;p.x=p1.x-p2.x;p.y=p1.y-p2.y;returnp;}main(){pointp1(10,10);p2(20,20);p1=p1+p2;p1.print();}
运算结果:3030注意1.为什么要用友元函数重载运算符2.=,(),[],→只能用成员函数重载3.<<,>>只能用友元函数重载4.友元函数重载单目运算符时要特别小心若单目运算符要改变对象的状态,且用友元函数重载该运算符,则应使用引用参数
三、几个特殊运算符的重载1.++和--前缀方式++i,--i后缀方式i++,i--(1)前缀方式
aa.operator++()成员函数重载operator++(X&aa)友元函数重载aa.operator--()成员函数重载operator--(X&aa)友元函数重载(2)后缀方式
aa.operator++(int)成员函数重载operator++(X&aa,int)友元函数重载aa.operator--(int)成员函数重载operator--(X&aa,int)友元函数重载
隐式调用a++但a++3;//error显式调用a.operator++(0)a.operator++(3);//ok=赋值运算符
不能被友元函数重载,不能被继承自动重载:系统定义人工重载:用户定义缺省拷贝构造函数:新建一个对象,再拷贝缺省赋值运算符重载函数:只拷贝(目的对象已存在)例
Aa; Ab=a; (调用缺省拷贝构造函数) Aa,b; b=a; (调用缺省赋值运算符重载载函数)缺省的成员函数重载赋值运算符X& X::operator=(constX&source){//按成员逐域复制}例1classstring{char*ptr;intlength;string&operator=(string&str);string(string&str);};string&string::operator=(string&str){if(length==0)deleteptr;ptr=newchar[str.length+1];strcpy(ptr,str.ptr);length=str.length;}现在可以使用:str1=str2例2指针悬挂问题classstring仅有缺省赋值运算符有时是不够的{char*contents;intsize;public:string(intsz){contents=newchar[sz];}~string(){deletecontents;}};没有重载赋值运算符main(){strings1(10),s2(20);s1=s2;//调用缺省赋值运算符}缺省赋值运算符的功能:逐域赋值(1)s2的contents->s1的contents(2)s2的size->s1的size解决方法:重载赋值运算符使其不仅能赋值成员contents,size,也要复制非成员(具体内容)加上:voidoperator=(string*);voidstring::operator=(string*str){if(this==str)return;deletecontents;contents=newchar[size=str→size];strcpy(contents,str→contents);}解决了指针悬挂问题3.()和[]函数调用运算符和下标运算符例1
classstring{char*ptr;public:string(constchar*);char&operator[](int);};char&string::operator[](inti){returnptr[i];}stringss=”asdf”;ss[1]=ss[3];相当于ss.operator[](1)=ss.operator[](3);ssS的新值为”afdf”
new和delete的重载(1)new和delete是运算符,可以重载(2)申请空间可以是基本内存,扩充内存、扩展内存、硬盘重载类型局部重载:在某一个类中进行,用成员函数重载运算符函数全局重载:在任何类外进行,用普通函数重载运算符函数
new可重载malloc()不可重载deletefree()void*operatornew(size-tsize,……){//完成分配工作returnpointer-to-memory;}voidoperatordelete(void*p,……){//释放由P指向的存贮空间}(1)对operatornew()来说,它的第一个参数必须是size-t类型,这是一个在标准头文件<stddef.h>中定义的与实现有关的整型类型,必须返回void*类型(2)对operatordelete来说,它的第一个参数必须是void*类型,第二个参数的类型是size-t(可选,删除对象的大小),必须返回void类型局部重载(1)类x的x::operatornew()类x的x::operatordelete()它们不是针对x类的对象操作的是静态成员函数(无论是否有static)(2)new在构造函数之前调用,分配构造函数建立X类对象的内存delete在析构函数之后调用,释放析构函数刚刚破坏的X类对象的内存(3)一个类只能声明一个operatordelete(),故operatordelete()不能进行函数重载一个类可以声明多个operatornew(),故operatornew()可以进行函数重载(4)重载的new()和delete()是可以继承的类型转换
一般类型转换隐式类型转换(标准类型转换)显式类型转换强制转换法函数转换法
强制法函数法inti,j;inti,j;cout<<(float)(i+j);cout<<(float(i+j)类类型转换构造函数转换函数标准类型标准类型:标准类型转换标准类型类类型:构造函数类类型标准类型:类型转换函数类类型类类型:类型转换函数一、构造函数
不能将类类型转换为类类型只能将标准类型转换为类类型不能将类类型转换为标准类型
条件:此类一定要有一个只带一个参数的构造函数二、转换函数特殊的成员函数(不能为友元函数),可以被继承、可以是虚函数、不能重载(函数重载),无参数,无返回类型,必须有返回函数,返回具有type类型的对象。X::operatorY(){Yy;……returny;}功能:X类类型标准类型X类类型Y类类型隐式使用:inti=a;显式使用:inti=a.operatorint();
一般使用隐式方式,仅当需要明确指出所使用的是哪一个转换函数时才使用显式方式
二义性问题若X类有X(int)构造函数operatorint()转换函数则inti=a;隐式调用转换函数Xa=i;隐式调用构造函数则语句a=a+i将会产生二义性,此时必须显式调用转换函数
用户定义的转换函数只有在无二义性时才能隐式使用解决方法(1)Xb=i; (2)a=a+X(i);a=a+b;说明:(1)编译程序先按用户定义的意思,再按标准格式,都不行,则失败(2)有二义性时,必须使用显式类型转换
(type)obi或type(obi)派生类提高软件的可重用性减少工作量避免编写重复代码减少出错机会继承是对象类间的一种相关关系,即派生关系,并具有以下性质:
类间的共享特性类间的细微区别类间的层次结构分类树反映了派生关系:最高层是最普遍最一般的,越往下反映的事物越具体,并且下层都包含了上层的特征派生类的概念
引入继承的目的在于为代码重用提供有效手段。一方面使用继承可以重用先前项目的代码,若原来的代码不能完全满足要求,还可以作少量的修改,满足不断变化的具体应用要求,从而提高程序设计的灵活性,避免不必要的重复设计单继承单继承:每个类可有多个派生类,但最多只有一个基类,从而形成一棵树结构多继承:每个类可有多个派生类,也可有多个基类,从而形成网状结构一、定义格式:class派生类名:继承方式基类名{<member-list>};其中:1.class关键字2.:将派生类与基类分开3.继承方式:publicprotectedprivate缺省:对于类等价于private对于结构等价于public四种继承方式1.公有派生class派生类名:public基类名{<member-list>};基类所有公有段成员成为派生类公有段成员基类所有保护段成员成为派生类保护段成员基类所有私有段成员不能被继承
即派生类的对象及其成员函数可直接访问基类的公有成员和保护成员2.私有派生class派生类名:private基类名{<member-list>};基类所有公有段成员成为派生类私有段成员基类所有保护段成员成为派生类私有段成员基类所有私有段成员不能被继承即派生类的成员函数可直接访问基类的公有成员和保护成员3.保护派生:class派生类名:protected基类名{<member-list>};基类所有公有段成员成为派生类保护段成员基类所有保护段成员成为派生类保护段成员基类所有私有段成员不能被继承
即派生类的成员函数可直接访问基类的公有成员和保护成员,还可往下继承4.部分公开(访问声明):为了使基类中的公有和保护成员在派生类中部分成为公有或保护成员,部分成为私有成员,可采用:基类名::成员名基类公有成员等价于派生类公有成员基类保护成员等价于派生类保护成员注意(1)并未引进新成员(2)在调整名字的访问时不能说明类型三、成员访问控制基类性质继承性质派生类性质
publicpublicpublicprotectedpublicprotectedprivatepublic不能访问publicprotectedprotectedprotectedprotectedprotectedprivateprotected不能访问publicprivateprivateprotectedprivateprivateprivateprivate不能访问
基类与派生类的关系 任何一个类都可以派生出一个新类,派生类也可以再派生出新类,因此,基类和派生类是相对而言的。一个基类可以是另一个基类的派生类,这样便形成了复杂的继承结构,出现了类的层次。一个基类派生出一个派生类,它又做另一个派生类的基类,则原来的基类为该派生类的间接基类。
1.派生类是基类的具体化。 2.派生类是基类定义的延续。 3.派生类是基类的组合。 派生类将其本身与基类区别开来的方法是添加数据成员和成员函数。因此,继承的机制将使得在创建新类时,只需说明新类与已有类的区别,从而大量原有的程序代码都可以复用。如:classA{
public:
voidf();};classB{
public:
voidf();
voidg();};classC:publicA,publicB{
public:
voidg();
voidh();};Cobj;则对函数f()的访问是二义的:obj.f();//无法确定访问A中或是B中的f()
赋值兼容规则 赋值兼容规则是指:在公有派生的情况下,一个派生类的对象可用于基类对象适用的地方。赋值兼容规则有三种情况(假定类derived由类base派生): (1)派生类的对象可以赋值给基类的对象。 derivedd; baseb; b=d; (2)派生类的对象可以初始化基类的引用。 derivedd; base&br=d;
(3)派生类的对象的地址可以赋给指向基类的指针。 derivedd; base*pb=&d; 多态性是面向对象程序设计的重要特征之一。所谓多态性是指当不同的对象收到相同的消息时,产生不同的动作。C++的多态性具体体现在运行和编译两个方面,在程序运行时的多态性通过继承和虚函数来体现,而在程序编译时多态性体现在函数和运算符的重载上。
运算符重载的实现 运算符的重载形式有两种:重载为类的成员函数和重载为类的友元函数。 运算符重载为类的成员函数的语法形式如下:<函数类型>operator<运算符>(<形参表>){<函数体>;}
friend<函数类型>operator<运算符>(<形参表>){
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2024河北省灵寿县农业技术中专学校工作人员招聘考试及答案
- 品质管理月度工作总结
- 阿里巴巴跨境电商战略合作合同范本
- 杭州房屋租赁合同范例
- 美容行业年终总结
- 计算机一级练习题库+参考答案
- 建筑设备租赁合同转让书
- 红色文化在中职教育中的应用
- 荔枝园土地承包合同
- 装修木工合同
- ISO 31000-2018 风险管理标准-中文版
- 双人法成生命支持评分表
- DBJ61_T 179-2021 房屋建筑与市政基础设施工程专业人员配备标准
- 毕业设计三交河煤矿2煤层开采初步设计
- 预应力锚索施工全套表格模板
- SIEMENS-S120变频器硬件、软件培训(高端培训)课件
- 食品流通许可证食品经营操作流程图
- CA6132普通车床使用说明书
- 风电场工作安全培训
- 压缩机课程设计(共28页)
- 四方公司机组扭振监测、控制和保护新技术---1105 17
评论
0/150
提交评论