c程序设计(part2).ppt
C+程序设计(part 2),OOP,Whatnon-OO Solution,#include#define STACK_SIZE 100struct Stack int top;int bufferSTACK_SIZE;void main()Stack st1,st2;st1.top=-1;st2.top=-1;int x;push(st1,12);pop(st1,x);,bool push(Stack,bool pop(Stack,st1.buffer2=-1;st2.buffer2+;,_,OOP,OO Solution,#include#define STACK_SIZE 100class Stack private:int top;int bufferSTACK_SIZE;public:Stack()top=-1;bool push(int i);bool pop(int,bool Stack:push(int i);if(top=STACK_SIZE-1)cout“Stack is overflow.n”;return false;else top+;buffer top=i;return true;,bool Stack:pop(int,#include#define STACK_SIZE 100struct Stack int top;int bufferSTACK_SIZE;void main()Stack st1,st2;st1.top=-1;st2.top=-1;int x;push(,X,bool push(Stack*const this,int i),bool pop(Stack*const this,int&i),this-,this-,this-,this-,this-,this-,this-,this-,st1.buffer2=-1;,Encapsulation,Information Hidding,OOP,ConceptsProgramObject1+Object2+ObjectnObject:Data+OperationMessage:function callClassObject-OrientedObject-BasedOnly Provide EncapsulationWithout InheritanceAda,etc,OOP,Why评价标准Efficency of DevelopmentQualityExternal Correctness、Efficiency、Robustness、Reliability Usability、ReusabilityInternal Readability、Maintainability、Portability,HUIUX,产品在规定的条件下和规定的时间内完成规定功能的能力(概率度量:可靠度),需求架构设计构建模式代码测试用例项目组织结构,OOP,Advantages 提高开发效率和软件质量更高层次的抽象数据抽象数据封装更好的模块化支持(高内聚、低耦合)软件复用(部分重用)对需求变更具有更好的适应性,C+vs Java,类,类定义 class;组成成员:成员变量、成员函数成员变量:在类定义中给出的是声明成员函数:在类定义中给出的是定义或声明类成员的访问控制描述,类,class TDate public:void SetDate(int y,int m,int d);int IsLeapYear();void Print();private:int year,month,day;,类定义不完整,类,成员变量声明 在类定义中对成员变量的说明是声明不能赋值 在类定义中声明成员变量时不能初始化引用或指针类型成员变量,类,class A;class B A a;/Error B b;/Error A*p;/OK B*q;/OK A/OK,类型还未定义/未定义完全 只能声明成指针或引用,类,成员函数在类定义中定义成员函数(inline)class TDate public:void SetDate(int y,int m,int d)year=y;month=m;day=d;int IsLeapYear()return(year%4=0,类,在类定义外定义成员函数,class TDate public:void SetDate(int y,int m,int d);void Print();private:int year,month,day;,void TDate:SetDate(int y,int m,int d)year=y;month=m;day=d;void TDate:Print()coutyear.month.dayendl;,a.h,a.cpp,类,成员函数的重载 遵循一般函数的重载规则class A public:void f();int f(int i);double f(double d);void main()A a;a.f();a.f(1);a.f(1.0);,类,对象属性和行为的封装体类的实例,属于值的范畴 类属于类型范畴,用于描述对象的属性,构造函数,对象的初始化描述与类同名、无返回类型自动调用,不可直接调用可重载默认构造函数:无参数的构造函数系统提供的默认构造函数当类中未提供构造函数时,编译系统自动提供参与对象的构造过程通常定义为 public有时也声明为私有的,其作用是限制创建该类对象的范围,即只能在本类和友元中创建该类对象,构造函数,构造函数的调用创建对象时,构造函数将自动被调用,所调用的构造函数在创建对象时指定 class A public:A();A(int i);A(char*p);A a1=A(1);A a1(1);A a1=1;/调A(int i)A a2=A();A a2;/调A(),注意:不能写成:A a2();A a3=A(“abcd”);A a3(“abcd”);A a3=“abcd”;/调A(char*)A a4;/调用a0、a1、a2、a3的A()A b5=A(),A(1),A(abcd),2,xyz“;,成员初始化表,成员初始化表定义构造函数时,函数头和函数体之间可以加入一个对数据成员进行初始化的表,用于对数据成员进行初始化 class A int x;const int y;int public:A():y(1),z(x),x(0);,成员初始化表,class A int m;public:A()m=0;A(int m1)m=m1;,class B int x;A a;public:B()x=0;B(int x1)x=x1;B(int x1,int m1):a(m1)x=x1;,void main()B b1;/调用B:B()和A:A()B b2(1);/调用B:B(int)和A:A()B b3(1,2);/调用B:B(int,int)和A:A(int),成员初始化表,在构造函数中尽量使用成员初始化表取代赋值动作const 成员/reference 成员/对象成员效率高数据成员太多时,不采用本条准则降低可维护性,成员初始化表,成员初始化表中的声明次序成员初始化的次序取决于它们在类定义中的声明次序,与它们在成员初始化表中的次序无关减轻编译系统的负担注意类成员定义的次序,class CString char*p;int size;public:CString(int x):size(x),p(new charsize);,?,析构函数,析构函数()对象消亡时,系统自动调用,class String char*str;public:String()str=NULL;String(char*p)str=new charstrlen(p)+1;strcpy(str,p);String()delete str;int length()return strlen(str);char get_char(int i)return stri;,Java:finalize(),RAII vs GCResource Acquisition Is Initialization,析构函数,void set_char(int i,char value)stri=value;char,拷贝构造函数,Copy Constructor创建一个对象时,用另一个同类的对象对其初始化自动调用 class A int x,y;char*p;public:A();A(const A/拷贝构造函数,const可有可无,拷贝构造函数和,三种情况将调拷贝构造函数case1A a;A b=a;case2f(A a);.A b;f(b);,拷贝构造函数,case3 A f()A a;return a;.f();如果类定义中没有给出拷贝构造函数,则编译系统提供一个默认拷贝构造函数默认拷贝构造函数的行为逐个成员初始化(member-wise initialization)如果相应的类有对象成员,则该定义是递归的,拷贝构造函数,class string char*p;public:string(char*str)p=new charstrlen(str)+1;strcpy(p,str);string()delete p;string s1(“abcd”);string s2=s1;,abcd,p,p,s1,s2,string:string(const string deep copy,悬挂指针,何时需要copy constructor?,abcd,动态对象,动态对象在堆(Heap)中创建的对象称为动态对象new/delete单个动态对象的创建与撤消 class A public:A();A(int);,为什么要引入new、delete操作符?,constructor/destructor,动态对象,A*p,*q;p=new A;在程序的堆中申请一块大小为sizeof(A)的空间调用A的默认构造函数对该空间上的对象初始化返回创建的对象的地址并赋值给pq=new A(1);调用A的另一个构造函数 A:A(int)delete p;调用p所指向的对象的析构函数释放对象空间 delete q;,动态对象,p=(A*)malloc(sizeof(A)free(p)malloc 不调用构造函数 free 不调析构函数new 操作符自动分配足够的空间自动返回指定类型的指针可以重载,动态对象数组,动态对象数组的创建与撤消A*p;p=new A100;delete p;注意不能显式初始化,相应的类必须有默认构造函数delete中的不能省,Const 成员,const 成员const 成员变量在成员变量的声明中加上const,则表示是常量class A const int x;初始化放在构造函数的成员初始化表中进行class A const int x;public:A(int c):x(c),Const 成员,const 成员函数class A int x,y;public:void f();const A a;a.f()合法?a是常量,在程序中不应改变a的值(改变a的成员变量的值)如果A:f中没有改变a的成员变量的值,则是合法的,否则是不合法的,Const 成员,编译程序往往无法知道函数f中是否改变了a的成员变量的值为了安全,编译程序认为a.f()不合法a.f()合法在A:f中不能改变a的成员变量的值在f的定义和声明处显式地指出之class A void f()const;,Const 成员,const 成员函数不能修改成员变量的值成员函数加上const 修饰符的作用调用点:告诉编译器该成员函数不会改变对象成员变量的值 定义点:告诉编译器该成员函数不应该改变对象成员变量的值,静态成员,静态成员类刻划了一组具有相同属性的对象对象是类的实例类中声明的成员变量属于实例化后的对象,有多个拷贝问题:同一个类的不同对象如何共享变量?如果把这些共享变量定义为全局变量,则缺乏数据保护,,静态成员,静态成员变量class A int x,y;static int shared;.;int A:shared=0;A a,b;类定义中声明的静态变量被该类的对象所共享类的静态成员变量只有一个拷贝静态变量也遵循类的访问控制,静态成员,静态成员函数class A static int shared;int x;public:static void f()shared void q()xshared;静态成员函数只能存取静态成员变量和调用静态成员函数静态成员函数也遵循类的访问控制,静态成员,静态成员的使用通过对象使用A a;a.f();通过类使用A:f();C+对类也是对象的观点的支持在一些面向对象程序设计语言(如:Smalltalk)中,把类也看成是对象,其属性由元类(meta class)来描述类定义中的静态成员就是属于类这个对象,静态成员,class A static int obj_count;public:A()obj_count+;A()obj_count-;static int get_num_of_obj()return obj_count;int A:obj_count=0;类A是对象obj_count:程序运行的某时刻存在的A类对象的数目get_num_of_obj:获取A类对象的数目的方法,示例,class singletonprotected:singleton()singleton(const singleton,singleton,Resource Control原则:谁创建,谁归还解决方法:自动归还,友元,友元类外部不能访问该类的private成员通过该类的public方法会降低对private成员的访问效率,缺乏灵活性例:矩阵类(Matrix)、向量类(Vector)和全局函数(multiply),全局函数实现矩阵和向量相乘,友元,int,class Matrix int*p_data;int lin,col;public:Matrix(int l,int c)lin=l;col=c;p_data=new intlin*col;Matrix()delete p_data;,友元,class Vector int*p_data;int num;public:Vector(int n)num=n;p_data=new intnum;Vector()delete p_data;,int,友元,void multiply(Matrix,友元,函数multiply中多次通过调用成员函数element访问m,v,r的元素在一个类定义中,可指定某全局函数、某其它类或其它类的成员函数具有访问该类的私有和保护成员的权限友元函数友元类友元类成员函数友元的作用提高面向对象程序设计的灵活性数据保护和对数据的存取效率之间的一个折中方案,友元,void func();class B;class C.void f();class A friend void func();/友元函数friend class B;/友元类friend void C:f();/友元类成员函数;,友元,class Matrix.friend void multiply(Matrix 友元不具有传递性示例:只能生成一个对象,原则,避免将data member放在公开接口中努力让接口完满(complete)且最小化,class AccessLevels public:int getReadOnly const return readOnly;void setReadWrite(int value)readWrite=value;int getReadWrite()return readWrite;void setWriteOnly(int value)writeOnly=value;private:int noAccess;int readOnly;int readWrite;int writeOnly;,继承,继承机制基于目标代码的复用对事物进行分类派生类是基类的具体化把事物(概念)以层次结构表示出来,有利于描述和解决问题增量开发,继承,基类与派生类(父类与子类)A是基类,B是派生类 class A/基类 int x,y;public:void f();void g();数据派生类对象拥有基类对象的所有成员新的成员,class B:public A/派生类 int z;public:void h();,继承,方法不同名同名 class B:public A public:void h()f(1);A:f();void f(int);重定义方法 虚函数,B b;b.f(1);/Okb.f();/Errorb.A:f();/Ok BAD,class A/基类 int x,y;public:void f();void g();,继承,protected继承方式publicprivate、protected访问控制调整A:f1,继承,派生类对象的初始化由基类和派生类共同完成基类成员:由基类的构造函数初始化派生类成员:由派生类的构造函数初始化构造函数的执行次序1.执行基类的构造函数2.执行派生类成员对象类的构造函数3.执行派生类的构造函数析构函数的执行次序与构造函数相反,继承,基类构造函数的调用缺省执行基类默认构造函数如果要执行基类的非默认构造函数,则必须在派生类构造函数的成员初始化表中指出,class A int x;public:A()x=0;A(int i)x=i;,class B:public A int y;public:B()y=0;B(int i)y=i;B(int i,int j):A(i)y=j;,B b1;/执行A:A()和B:B()B b2(1);/执行A:A()和B:B(int)B b3(0,1);/执行A:A(int)和B:B(int,int),虚函数,类型相容类、类型类型相容、赋值相容问题:a、b是什么类型时,a=b 合法?A a;B b;class B:public A对象的身份发生变化属于派生类的属性已不存在B*pb;A*pa=pb;class B:public A B b;A class B:public A 对象身份没有发生变化,虚函数,class A int x,y;public:void f();class B:public A int z;public:void f();void g();,A a;B b;a=b;/OK,b=a;/Errora.f();/A:f(),A/Error,把派生类对象赋值给基类对象,基类的引用或指针可以引用或指向派生类对象,func1(A,A:f?B:f?,虚函数,前期绑定(Early Binding)编译时刻依据对象的静态类型效率高、灵活性差动态绑定(Late Binding)运行时刻依据对象的实际类型(动态)灵活性高、效率低注重效率默认前期绑定后期绑定需显式指出virtual,虚函数,定义virtualclass A public:virtual void f();动态绑定 根据实际引用和指向的对象类型方法重定义,虚函数,如基类中被定义为虚成员函数,则派生类中对其重定义的成员函数均为虚函数限制类的成员函数才可以是虚函数静态成员函数不能是虚函数内联成员函数不能是虚函数构造函数不能是虚函数析构函数可以(往往)是虚函数,虚函数,后期绑定的实现,class A int x,y;public:virtual f();virtual g();h();class B:public A int z;public:f();h();A a;B b;A*p;,创建一个虚函数表(vtable)记录所有虚函数入口地址对象的内存空间中含有指针,指向其虚函数表,B:f,A:g,b,x,y,z,B_vtable,p-f()(*(char*)p-4)(p),a,x,y,A:f,A:g,A_vtable,虚函数,class A public:A()f();virtual void f();void g();void h()f();g();class B:public A public:void f();void g();,B b;A*p=,/A:A(),A:f,B:B(),/B:f,/A:g,/A:h,B:f,A:g,class A public:virtual void f();void g();class B:public A public:void f()g();void g();B b;A*p=,/b.B:g,虚函数,纯虚函数和抽象类纯虚函数声明时在函数原型后面加上=0 virtual int f()=0;往往只给出函数声明,不给出实现抽象类至少包含一个纯虚函数不能用于创建对象为派生类提供框架,派生类提供抽象基类的所有成员函数的实现,class AbstractClass public:virtual int f()=0;,虚函数,Figure,Rectangle,Ellipse,Line,virtual display()=0;,display,display,display,Figure*a100;a0=new Rectangle();a1=new Ellipse();a2=new Line();for(int i=0;idisplay();,class Button;/Abstract Class class MacButton:public Button;class WinButton:public Button;class Label;/Abstract Class class MacLabel:public Label;class WinLabel:public Label;,class AbstractFactory public:virtual Button*CreateButton()=0;virtual Label*CreateLabel()=0;class MacFactory:public AbstractFactory public:MacButton*CreateButton()return new MacButton;MacLabel*CreateLabel()return new MacLabel;class WinFactory:public AbstractFactory public:WinButton*CreateButton()return new WinButton;WinLabel*CreateLabel()return new WinLabel;,AbstractFactory*fac;switch(style)case MAC:fac=new MacFactory;break;case WIN:fac=new WinFactory;break;Button*button=fac-CreateButton();Label*Label=fac-CreateLabel();,抽象工厂模式Abstact Factory,虚函数,虚析构函数,class B;class D:public B;B*p=new D;?:delete p;,class string class B class D:public Bstring name;B*p=new D;?:delete p;,虚函数,确定public inheritance,是真正意义的“is_a”关系不要定义与继承而来的非虚成员函数同名的成员函数,class B public:void mf();class D:public B;D x;B*pB=,public:void mf();,/B:mf,/D:mf,class rectangle public:void setHeight(int);void setWidth(int);int height()const;int width()const;class Square:public rectangle public:void setLength(int);,virtual,virtual,private:void setHeight(int);void setWidth(int);,Penguin,class FlyingBirdclass NonFlyingBird,virtual void fly()error(Penguins cant fly!);,void makeBigger(Rectangle,assert(s.width()=s.height();,虚函数,明智地运用private InheritanceImplemented-in-term-of需要使用Base Class中的protected成员,或重载virtual function不希望一个Base Class被client使用在软件”设计”层面无意义,只用于软件实现层面,class CHumanBeing;class CStudent:private CHumanBeing;void eat(const CHumanBeing/Error,虚函数,纯虚函数只有函数接口会被继承子类必须继承函数接口(必须)提供实现代码一般虚函数函数的接口及缺省实现代码都会被继承子类必须继承函数接口可以继承缺省实现代码非虚函数函数的接口和其实现代码都会被继承必须同时继承接口和实现代码,class Shape public:virtual void draw()const=0;virtual void error(const string,虚函数,绝对不要重新定义继承而来的缺省参数值静态绑定效率,class Apublic:virtual void f(int x=0);class B:public Apublic:virtual void f(int x=1);,class C:public Apublic:virtual void f(int);,A*p_a;B b;p_a=,A*p_a1;C c;p_a1=,对象中只记录虚函数的入口地址,多继承,多继承定义class:,成员表继承方式public、private、protected继承方式及访问控制的规定同单继承派生类拥有所有基类的所有成员,多继承,Bedweight,Sleep(),SetWeight(),Sofaweight,WatchTV(),SetWeight(),SleepSofa,foldOut(),多继承,Furnitureweight,SetWeight(),Furnitureweight,SetWeight(),Bed,Sleep(),Sofa,WatchTV(),SleepSofa,FoldOut(),Base-ClassDecomposition,多继承,Furnitureweight,SetWeight(),Bed,Sleep(),Sofa,WatchTV(),SleepSofa,FoldOut(),Virtual Inheritance,多继承,基类的声明次序决定:对基类构造函数/析构函数的调用次序对基类数据成员的存储安排名冲突:虚基类如果直接基类有公共的基类,则该公共基类中的成员变量在多继承的派生类中有多个副本,D,A,B,C,class A int x;class B:A;class C:A;class D:B,C;,多继承,类D拥有两个x成员:B:x和C:x虚基类合并class A;class B:virtual A;class C:virtual A;class D:B,C;注意虚基类的构造函数由最新派生出的类的构造函数调用虚基类的构造函数优先非虚基类的构造函数执行,