C7对象和类教学课件

上传人:文**** 文档编号:240745048 上传时间:2024-05-04 格式:PPT 页数:70 大小:1.60MB
返回 下载 相关 举报
C7对象和类教学课件_第1页
第1页 / 共70页
C7对象和类教学课件_第2页
第2页 / 共70页
C7对象和类教学课件_第3页
第3页 / 共70页
点击查看更多>>
资源描述
C7对象和类对象和类21、静念园林好,人间良可辞。22、步步寻往迹,有处特依依。23、望云惭高鸟,临木愧游鱼。24、结庐在人境,而无车马喧;问君何能尔?心远地自偏。25、人生归有道,衣食固其端。提倡多文件的程序结构文件是程序的编译单位,可以实现单独编译一个文件修改后不用重新编译整个程序,有利于大程序的管理有利于程序的安全,是封装的一种手段,把实现细节与抽象分开程序的结构更清晰头文件中常包含的内容:函数原型符号常量(用#define或const定义)结构声明类生明模板声明内联函数(普通函数定义不可)OOP通常的做法是一个头文件中放置类定义,另一个文件(源文件)放置类中方法的定义一个坐标转换的例子:/coordin.h-structure templates and function prototypes/structure templates#ifndef COORDIN_H_#define COORDIN_H_struct polar double distance;/distance from origin double angle;/direction from origin;struct rect double x;/horizontal distance from origin double y;/vertical distance from origin;/prototypespolar rect_to_polar(rect xypos);void show_polar(polar dapos);#endif/file1.cpp-example of a three-file program#include#include coordin.h/structure templates,function prototypesusing namespace std;int main()rect rplace;polar pplace;cout rplace.x rplace.y)/slick use of cin pplace=rect_to_polar(rplace);show_polar(pplace);cout Next two numbers(q to quit):;cout Bye!n;return 0;/file2.cpp-contains functions called in file1.cpp#include#include#include coordin.h/structure templates,function prototypes/convert rectangular to polar coordinatespolar rect_to_polar(rect xypos)using namespace std;polar answer;answer.distance=sqrt(xypos.x*xypos.x+xypos.y*xypos.y);answer.angle=std:atan2(xypos.y,xypos.x);return answer;/returns a polar structure/show polar coordinates,converting angle to degreesvoid show_polar(polar dapos)using namespace std;const double Rad_to_deg=57.29577951;cout distance=dapos.distance;cout ,angle=dapos.angle*Rad_to_deg;cout degreesn;7.2 类定义类定义包含两部分类头(class head,由关键字class 及其后面的类名构成),类体(class body,由一对花括号包围起来)。类定义后面必须接一个分号或一列声明class Screen /*.*/;class Screen /*.*/myScreen,yourScreen;在类体中对类的数据成员和成员函数进行声明,并指定这些类成员的访问级别。类体定义了类成员表每个类定义引入一个不同的类类型,即使两个类类型具有完全相同的成员表,它们仍是不同的类型class First int memi;double memd;class Second int memi;double memd;class First obj1;Second obj2=obj1;/错误:obj1 和 obj2 类型不同类体定义了一个域,在类体中的类成员声明把这些成员名字引入到它们的类域中可以以两种方式引用这种类类型1 指定关键字class,后面紧跟类名class First obj1;2 只指定类名Second obj2;数据成员类数据成员的声明方式同变量声明相同。例如Screen 类可以有下列数据成员#include class Screen string _screen;/string(_height*_width)string:size_type _cursor;/当前屏幕Screen 位置 short _height;/行数 short _width;/列数;数据成员_screen 的类型是string_cursor 是string 数据成员的索引,它指向当前的Screen 位置,类型是string:size_type数据成员可以是任意类型,如类数据成员可以是任意类型例如class StackScreen int topStack;void(*handler)();/函数的指针vector stack;/类的 vector;也可以是静态数据成员(static)除了静态 数据成员外,数据成员不能在类体中被显式地初始化,例如class First int memi=0;/错误 double memd=0.0;/错误;用户会希望在所定义类型的对象上执行各种各样的操作,这些操作可以用类成员函数来实现类的成员函数被声明在类体中class Screen public:void home();void move(int,int);char get();char get(int,int);bool checkRange(int,int);/.;成员函数成员函数的定义也可以被放在类体内,如class Screen public:/home()and get()的定义 void home()_cursor=0;char get()return _screen_cursor;/.;成员函数被声明在它的类中,该成员函数名在类域之外是不可见的,可以通过点.或箭头-成员访问操作符引用成员函数:ptrScreen-home();myScreen.home();成员函数拥有访问该类的公有和私有成员的特权。而一般来说普通函数只能访问类的公有成员成员函数可以是重载的函数,但是一个成员函数只能重载自己类的其他成员函数class Screen public:/重载成员函数 get()的声明char get()return _screen_cursor;char get(int,int);/.;const 成员函数const Screen blankScreen;blankScreen.display();/读类对象blankScreen.set(*);/错误:修改类对象通过把成员函数声明为const 以表明它不修改类对象const 类对象只能调用被声明为const 的成员函数修改类数据成员的函数声明为const 是非法的一般来说,如果一个类的成员函数不修改类数据成员,就应该把那些声明为const 成员函数class Screen public:bool isEqual(char ch)const;/.private:string:size_type _cursor;string _screen;/.;bool Screen:isEqual(char ch)constreturn ch=_screen_cursor;成员访问信息隐藏是为了防止程序的函数直接访问类类型的内部表示而提供的一种形式化机制,类成员的访问限制是通过类体内被标记为public,private 以及protected(访问限定符)来指定的公有成员public member,在程序的任何地方都可以被访问。成员函数通常被指定为公有成员,它们定义了可以被一般程序用来操纵该类类型对象的操作,即类的公共接口。但有些表示类的内部实现细节的成员函数可以例外私有成员private member,只能被成员函数和类的友元访问。类中的数据成员通常声明为private被保护成员protected member,对派生类就像public 成员一样,对其他程序则表现得像private一个类可以包含多个访问限制区,每个区一直有效直到另一个区标签或类体的结束为止。如果没有指定访问限定符,则缺省情况下在类体的开始左括号后面的区是private 区例子:设计一个股票类型考虑用一个对象表示某人所持的一种股票的情况:所需操作:获得股票增持卖出更新价格显示股票信息存储信息:公司名称数量单股价格总价格一般说来,定义一个类可以有两个步骤组成:类结构定义和类方法定义。#include#include class Stock /class declarationprivate:char company30;int shares;/数量 double share_val;double total_val;void set_tot()total_val=shares*share_val;public:void acquire(const char*co,int n,double pr);void buy(int num,double price);void sell(int num,double price);void update(double price);void show();类设计应尽量把实现细节与公有接口分开公有接口表示设计的抽象组件。将实现细节放在一起,并于抽象分开成为封装。C+的类机制中有三种封装:数据隐藏,将数据放在类的私有部分;将实现细节放在私有部分将成员函数的定义与类结构定义放在不同文件中实现细节:类的成员函数使用作用于解析操作符:类方法可以访问类的私有组件实现的代码见stock.cpp文件成员函数说明定义位于类声明中的函数自动成为内联函数。所以set_tot()是个内联函数。内联函数也可以使用如下方法定义:class Stock /class declarationprivate:/void set_tot();public:/;inline void set_tot()total_val=shares*share_val;可以通过显式地在类体中出现的函数声明上使用关键字inline,或者通过在类体外出现的函数定义上显式使用关键字inline,或者两者都用调用函数比直接计算操作符要慢得多:不但必须拷贝实参,保存机器的寄存器,程序还必须转向一个新位置。若一个函数被指定为inline 函数,则它将在程序中每个调用点上被内联地展开,例如set_tot();编译时被展开为total_val=shares*share_val;但是注意inline 指示对编译器来说只是一个建议,编译器可以选择忽略该建议。因为把一个函数声明为inline 函数并不见得真的适合在调用点上展开内联函数的规则要求使用它的每个文件都要对其进行定义。对于同一程序的不同文件如果inline 函数出现,其定义必须相同为确保内联定义对多文件程序中的所有文件可用,最简单的方法是将其放在类定义的头文件中四个成员函数都设置或修改了total_val的值,都是调用函数set_tot()。该函数只是实现代码的一种方式,而不是公共接口,所以将其声明为私有。方法使用哪个对象?调用成员函数时,调用的是一个具体对象(即调用它的对象)的数据成员Stock kate;kate.sell();sell()操作的数据成员是对象kate的数据成员。使用类C+的目标是使得使用类与使用基本内置类型尽可能相同。创建类对象可以声明类的变量(对象),可以使用new操作符分配存储空间,可以把类对象作为函数的参数和返回值,可以把一个对象赋值给另一个对象等等。文件stock.cpp给出了一个使用类的完整的例子7.3类的初始化内部类型或结构可以方便的初始化,例如int year=2001;struct thing char*pn;int m;thing something=“Beijing”,23.5;/以下的初始化会出错Stock kate=“Sukie,Inc.”,200,33.56;/原因是类的数据部分访问是私有的,程序不可直接访 /数据成员构造函数类初始化机制是构造函数,它保证在每个对象的首次使用之前被编译器自动应用在每个类对象上-准确地说,C+提供了名称和使用句法,程序员提供方法的定义构造函数与类同名,以此来标识构造函数构造函数不能指定返回类型class Account public:/缺省构造函数Account();/.private:char*_name;unsigned int _acct_nmbr;double _balance;C+语言对于一个类可以声明多少个构造函数没有限制,只要每个构造函数的参数表惟一的即可在最小情况下,必须允许用户为每一个需要设置的数据成员提供一个初始值。例如一个账户号码要能被设置或自动生成来保证其惟一性Account(const char*name,double open_balance);用户可能只要求指定一个名字,让构造函数自动把_balance 初始化为0Account(const char*name);Account(const char*name,double open_balance=0.0);Account类定义可能具有如下的结构:class Account public:/缺省构造函数Account();/声明中的参数名不是必需的Account(const char*,double=0.0);const char*name()return _name;/.private:/.;合法的Account 类对象定义int main()/ok:都调用双参数构造函数Account acct(Ethan Stern);Account*pact=new Account(Michael Lieberman,5000);if(strcmp(acct.name(),pact-name()/.在类对象首次被使用之前,构造函数将被应用在该对象上为构造函数指定实参有三种等价形式/一般等价的形式Account acct1(Anna Press);Account acct2=Account(Anna Press);Account acct3=Anna Press;acct3 的形式只能被用于指定单个实参的情形一般来说,推荐使用acct1 的形式下面为Stock类定义一个构造函数 Stock(const char*co,int n=0,double pr=0.0);/在类定义中声明的 /原型/定义构造函数Stock:Stock(const char*co,int n,double pr)std:cout Constructor using co calledn;std:strncpy(company,co,29);company29=0;if(n 0)std:cerr Number of shares cant be negative;company shares set to 0.n;shares=0;else shares=n;share_val=pr;set_tot();注意:构造函数声明中的参数名可以不写;构造函数定义中的参数名不能使用类成员名,例如class Stockpublic:Stock(const char*co,int n,double pr);/private:char co 30;int n;double pr;/错误:会出现n=n;pr=pr;这样的赋值 Stock:Stock(const char*co,int n,double pr)缺省构造函数缺省构造函数是指不需要用户指定实参就能够被调用的构造函数。即它用于如下形式Stock stock1;一般的,设计类时应当提供对所有类成员作隐式初始化的缺省构造函数1 给已有构造函数的所有参数提供默认值:Stock(const char*co=“Error”,int n=0,double pr=0.0);2 定义没有参数的构造函数Stock:Stock()/default constructor std:cout Default constructor calledn;std:strcpy(company,no name);shares=0;share_val=0.0;total_val=0.0;有了缺省构造函数之后就可以用以下的方式创建类对象Stock first;Stock second=Stock();Stock*pstock=new Stock;要注意区别下列的形式:Stock first(“Sukie,Inc.”);Stock second();Stock third;成员初始化表类的初始化有一种可替换的语法:成员初始化表,是由逗号分开的成员名及其初值的列表/Account缺省构造函数inline Account:Account()_name=0;_balance=0.0;_acct_nmbr=0;可以由如下的成员初始化表替代/使用成员初始化表的缺省 Account 构造函数inline Account:Account():_name(0),_balance(0.0),_acct_nmbr(0)成员初始化表只能在构造函数定义中被指定,该初始化表被放在参数表和构造函数体之间,由冒号开始inline Account:Account(const char*name,double opening_bal):_balance(opening_bal)_name=new char strlen(name)+1;strcpy(_name,name);_acct_nmbr=get_unique_acct_nmbr();拷贝构造函数用一个类对象初始化该类的另一个对象被称为缺省按成员初始化一个类对象向该类的另一个对象作拷贝,是通过依次拷贝每个非静态数据成员来实现的如果定义了拷贝构造函数,则在用一个类对象初始化该类另一个对象时它就会被调用,来替代缺省的行为缺省按成员的初始化对于类的正确行为常常是不合适的拷贝构造函数有一个指向类对象的引用作为形式参数,传统上被声明为constinline Account:Account(const Accout&rhs):_balance(rhs._balance)_name=new char strlen(rhs._name)+1;strcpy(_name,rhs._name);/不能拷贝 rhs._acct_nmbr _acct_nmbr=get_unique_acct_nmbr();7.4 析构函数构造函数的目的之一是为了自动获取资源,如通过应用new 表达式分配了一个字符数组;析构函数是一种对称的操作,它为生命期即将结束的类对象返还相关的资源或者自动释放资源析构函数的名字是在类名前加上波浪线,它不返回任何值也没有任何参数不能被重载Inline Account:Account()delete _name;return_acct_nmbr(_acct_nmbr);析构函数的自动执行时间:静态存储类对象,程序结束时自动存储对象,定义对象的代码块执行完毕时用new创建的对象,在使用delete收回内存时析构函数并不是必需的考虑下面的类class Point3d public:/.private:float x,y,z;一般地,如果一个类的数据成员是按值存储的,则无需析构函数在Stock中,析构函数不需要做什么事情,可以写成这样来验证一下/class destructorStock:Stock()/verbose class destructor std:cout Bye,company B.total()?coutA is bigger.:couttopval)return s;else return?为此C+引入了this指针,指向用来调用成员函数的对象。const Stock&Stock:topval(const Stock&s)const if(svaltopval)return s;else return*this;注意:每个成员函数(包括构造函数和析构函数)中都包含一个this指针该指针指向了成员函数的调用者(类对象)。同一个类创建多个对象,每个对象在调用成员函数时,this都指向该调用对象this指针的使用方法this-m_member(*this).m_member加入了成员函数topval的类定义和其实现见文件stock2.h,stock2.cpp7.6 对象数组可以用类类型创建数组,就像使用内置数据类型一样:Stock myarr3;声明了对象数组以后可以访问其成员函数:myarr0.update();myarr1.show();Stock top1=myarr2val(myarr1);可见,对对象数组元素的使用,与使用类对象的方法是一样的。1 如果类中没有声明任何构造函数,使用如下方式:class Aprivate:int x;A arr3;此时创建的对象没有被初始化。对象数组的创建方式2 如果声明了带参数的构造函数,但没有声明缺省构造函数,则使用第一种方法会出错,而应该使用构造函数初始化元素:class Aprivate:int x;double y;public:A(int a,double b);A arr3;/出错,这与创建单个对象是一致的A arr3=A(10,12.5),/以合法的构造函数初始化 A(32,10.87),A(5,19.09);此时,分别按顺序初始化了3个数组元素。注意,必须为每个元素初始化。3 如果类中有多个构造函数,但是没有缺省构造函数,则必须为每个元素初始化,可以使用不同的构造函数:class Aprivate:int x;double y;public:A(int a)x=a;y=0.999;A(double b)x=0;y=b;void show()coutx=xt;couty=yendl;int main()A arr3=A(5),A(0.85),A(16);arr0.show();arr1.show();arr2.show();4 如果类中有构造函数,并且定义了缺省构造函数,则可以不初始化数组元素,或只初始化一部分元素class Aprivate:int x;double y;public:A()x=0;y=0.0;A(int a)x=a;y=0.999;A(double b)x=0;y=b;void show()coutx=xt;couty=yendl;int main()A arr6=A(5),A(0.85),A(),A(16);arr0.show();arr2.show();arr3.show();arr4.show();A newarr3;newarr1.show();Stock类对象数组的使用见文件usestok2.cpp实现变更与接口变更Stock类仍有不足之处。例如股票的名称被限制为最多29个字符,可以将私有成员的类型由字符数组改为string类型。这需要3处改动:头文件中加入包含类定义中构造函数的定义这种修改属于实现变更,不会影响到类的使用者。还有一种变更是接口变更,这会影响到类的使用者。例如增加一个构造函数:Stock(const string&co,int n,double pr);可供用户使用的方法增多了,例如sting co_name;getline(cin,co_name);Stock newstock(co_name,100,13.5);类作用域在类中声明的名称作用域为整个类,即包括类声明和成员函数的定义区域,称为类作用域。所以在类声明或成员函数定义中可以直接使用类中声明的标识符在其它情况下,要使用成员名称必须加以修饰。共有3种修饰符:直接成员操作符,间接成员操作符和作用域解析操作符唯一例外是构造函数的使用,因为其名字和类名是一样的Stock mystock=Stock();成员访问操作符(.,-)以及域解析操作符(:)可以被用在程序中来访问类域中声明的成员,编译器将在这个类的类域中查找该标识符若程序代码本身就在类域中,则这些程序部分可以直接访问类成员类声明的代码类成员定义的代码类作用域中的常量。当类中需要常量时下面做法是错误的class Stock/private:const int LEN=30;/错误,成员不能初始化/因为类声明只描述对象的形式,没有创建对象/之前并没有存储空间 char company LEN;int n;double pr;可以用两种方法解决这个问题用枚举class Stock/private:enum LEN=30;char company LEN;注意,这里枚举并不创建类的成员,即对象中不会包含枚举。LEN是一个标识符,其作用域为整个类。在该类作用域中,编译器以30替换之。另一种方法是在类中定义静态常量static const int LEN=30;7.7 一个例子:堆栈堆栈是一种常用的数据类型。其特点是用LIFO的方式管理数据项。堆栈应具有如下的操作:可以创建空堆栈将数据项添加到栈顶将数据项从栈顶删除察看栈是否为空察看栈是否为满#ifndef STACK_H_#define STACK_H_typedef unsigned long Item;class Stackprivate:enum MAX=10;/constant specific to class Item itemsMAX;/holds stack items int top;/index for top stack itempublic:Stack();bool isempty()const;bool isfull()const;/push()returns false if stack already is full,true otherwise bool push(const Item&item);/add item to stack /pop()returns false if stack already is empty,true otherwise bool pop(Item&item);/pop top into item;#endif成员函数的定义Stack:Stack()/create an empty stack top=0;bool Stack:isempty()const return top=0;bool Stack:isfull()const return top=MAX;bool Stack:push(const Item&item)if(top 0)item=items-top;return true;else return false;56、书不仅是生活,而且是现在、过去和未来文化生活的源泉。库法耶夫57、生命不可能有两次,但许多人连一次也不善于度过。吕凯特58、问渠哪得清如许,为有源头活水来。朱熹59、我的努力求学没有得到别的好处,只不过是愈来愈发觉自己的无知。笛卡儿60、生活的道路一旦选定,就要勇敢地走到底,决不回头。左拉
展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 办公文档 > 教学培训


copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!