C++ 7对象和类_第1页
C++ 7对象和类_第2页
C++ 7对象和类_第3页
C++ 7对象和类_第4页
C++ 7对象和类_第5页
已阅读5页,还剩63页未读 继续免费阅读

下载本文档

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

文档简介

1、第7章 对象和类17.1 过程性编程和面向对象编程面向对象编程是一种设计程序的概念性方法,它的主要特点如下:抽象封装和数据隐藏多态继承代码的可重用性2例如,用计算机统计一个篮球队中各球员的技术数据面向过程的程序员:要输入球员的姓名,投篮次数,命中率等重要数据,其中一些数据如命中率由计算机来计算程序能够显示这些数据让main函数调用一个函数来实现输入,再调用一个函数来实现计算,调用第三个函数显示结果。下一场比赛呢?可以再用一个函数更新统计数据。Main函数中需要放一个菜单,以让用户选择输入,计算,显示结果或更新数据表示数据:用数组或结构总之,首先考虑要遵循的步骤,然后考虑如何表示数据3面向对象的

2、程序员如下考虑:我要跟踪的是球员,要有一个对象表示选手的姓名和各种统计数据需要一些方法:首先是把基本信息加入到这些单元中去-初始化;需要一些计算方法,如命中率,程序应自动执行这些方法,无需用户干预;更新和显示信息的方法所以,用户和数据交互的方式有三种:初始化,更新和报告-用户接口总之,使用面向对象的方法时首先从用户的角度考虑对象-描述对象所需的数据以及描述用户与数据交互所需的操作。4提倡多文件的程序结构文件是程序的编译单位,可以实现单独编译一个文件修改后不用重新编译整个程序,有利于大程序的管理有利于程序的安全,是封装的一种手段,把实现细节与抽象分开程序的结构更清晰5头文件中常包含的内容:函数原

3、型符号常量(用#define或const定义)结构声明类生明模板声明内联函数(普通函数定义不可)OOP通常的做法是一个头文件中放置类定义,另一个文件(源文件)放置类中方法的定义一个坐标转换的例子:6/ 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 ori

4、gin;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); #endif7/ - example of a three-#include #include coordin.h / structure templates, function prototypesusing namespace std;int

5、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;8/ - contains functions called in #include #include #include coordin.h / structure templates, function prototypes

6、/ 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

7、 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;97.2 类定义类定义包含两部分类头(class head,由关键字class 及其后面的类名构成),类体(class body,由一对花括号包围起来)。类定义后面必须接一个分号或一列声明class Screen /* .

8、*/ ;class Screen /* . */ myScreen, yourScreen;在类体中对类的数据成员和成员函数进行声明,并指定这些类成员的访问级别。类体定义了类成员表10每个类定义引入一个不同的类类型,即使两个类类型具有完全相同的成员表,它们仍是不同的类型class First int memi; double memd;class Second int memi; double memd;class First obj1;Second obj2 = obj1; / 错误: obj1 和 obj2 类型不同11类体定义了一个域,在类体中的类成员声明把这些成员名字引入到它们的类域中

9、可以以两种方式引用这种类类型1 指定关键字class, 后面紧跟类名class First obj1;2 只指定类名Second obj2;12数据成员类数据成员的声明方式同变量声明相同。例如Screen 类可以有下列数据成员#include class Screen string _screen; / string( _height * _width ) string:size_type _cursor; / 当前屏幕Screen 位置 short _height; / 行数 short _width; / 列数;数据成员_screen 的类型是string_cursor 是string 数

10、据成员的索引,它指向当前的Screen 位置,类型是string:size_type13数据成员可以是任意类型,如类数据成员可以是任意类型例如class StackScreen int topStack;void (*handler)(); / 函数的指针vector stack; / 类的 vector;也可以是静态数据成员(static)除了静态 数据成员外,数据成员不能在类体中被显式地初始化,例如class First int memi = 0; / 错误 double memd = 0.0; / 错误;14用户会希望在所定义类型的对象上执行各种各样的操作,这些操作可以用类成员函数来实现

11、类的成员函数被声明在类体中class Screen public: void home(); void move( int, int ); char get(); char get( int, int ); bool checkRange( int, int ); / .;成员函数15成员函数的定义也可以被放在类体内,如class Screen public: / home() and get() 的定义 void home() _cursor = 0; char get() return _screen_cursor; / .;成员函数被声明在它的类中,该成员函数名在类域之外是不可见的,可以

12、通过点. 或箭头- 成员访问操作符引用成员函数:ptrScreen-home();myScreen.home();16成员函数拥有访问该类的公有和私有成员的特权。而一般来说普通函数只能访问类的公有成员成员函数可以是重载的函数,但是一个成员函数只能重载自己类的其他成员函数class Screen public:/ 重载成员函数 get() 的声明char get() return _screen_cursor; char get( int, int );/ .;17const 成员函数const Screen blankScreen;blankScreen.display(); / 读类对象bl

13、ankScreen.set( * ); / 错误: 修改类对象通过把成员函数声明为const 以表明它不修改类对象const 类对象只能调用被声明为const 的成员函数修改类数据成员的函数声明为const 是非法的一般来说,如果一个类的成员函数不修改类数据成员,就应该把那些声明为const 成员函数18class Screen public:bool isEqual( char ch ) const;/ .private:string:size_type _cursor;string _screen;/ .;bool Screen:isEqual( char ch ) constreturn

14、 ch = _screen_cursor;19成员访问信息隐藏是为了防止程序的函数直接访问类类型的内部表示而提供的一种形式化机制,类成员的访问限制是通过类体内被标记为public, private 以及protected(访问限定符)来指定的公有成员public member ,在程序的任何地方都可以被访问。成员函数通常被指定为公有成员,它们定义了可以被一般程序用来操纵该类类型对象的操作,即类的公共接口。但有些表示类的内部实现细节的成员函数可以例外20私有成员private member, 只能被成员函数和类的友元访问。类中的数据成员通常声明为private被保护成员protected mem

15、ber ,对派生类就像public 成员一样,对其他程序则表现得像private一个类可以包含多个访问限制区,每个区一直有效直到另一个区标签或类体的结束为止。如果没有指定访问限定符,则缺省情况下在类体的开始左括号后面的区是private 区21例子:设计一个股票类型考虑用一个对象表示某人所持的一种股票的情况:所需操作:获得股票增持卖出更新价格显示股票信息存储信息:公司名称数量单股价格总价格22一般说来,定义一个类可以有两个步骤组成:类结构定义和类方法定义。#include #include class Stock / class declarationprivate: char company

16、30; 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();23类设计应尽量把实现细节与公有接口分开公有接口表示设计的抽象组件。

17、将实现细节放在一起,并于抽象分开成为封装。C+的类机制中有三种封装:数据隐藏,将数据放在类的私有部分;将实现细节放在私有部分将成员函数的定义与类结构定义放在不同文件中24实现细节:类的成员函数使用作用于解析操作符:类方法可以访问类的私有组件实现的代码见stock.cpp文件25成员函数说明定义位于类声明中的函数自动成为内联函数。所以set_tot() 是个内联函数。内联函数也可以使用如下方法定义:class Stock / class declarationprivate: / void set_tot() ;public: /;inline void set_tot() total_val

18、= shares * share_val; 26可以通过显式地在类体中出现的函数声明上使用关键字inline ,或者通过在类体外出现的函数定义上显式使用关键字inline ,或者两者都用调用函数比直接计算操作符要慢得多:不但必须拷贝实参,保存机器的寄存器,程序还必须转向一个新位置。若一个函数被指定为inline 函数,则它将在程序中每个调用点上被内联地展开,例如set_tot(); 编译时被展开为total_val = shares * share_val; 27但是注意inline 指示对编译器来说只是一个建议,编译器可以选择忽略该建议。因为把一个函数声明为inline 函数并不见得真的适合

19、在调用点上展开内联函数的规则要求使用它的每个文件都要对其进行定义。对于同一程序的不同文件如果inline 函数出现,其定义必须相同为确保内联定义对多文件程序中的所有文件可用,最简单的方法是将其放在类定义的头文件中28四个成员函数都设置或修改了total_val的值,都是调用函数set_tot()。该函数只是实现代码的一种方式,而不是公共接口,所以将其声明为私有。方法使用哪个对象?调用成员函数时,调用的是一个具体对象(即调用它的对象)的数据成员Stock kate;kate. sell();sell()操作的数据成员是对象kate的数据成员。29使用类C+的目标是使得使用类与使用基本内置类型尽可

20、能相同。创建类对象可以声明类的变量(对象),可以使用new操作符分配存储空间,可以把类对象作为函数的参数和返回值,可以把一个对象赋值给另一个对象等等。文件stock.cpp给出了一个使用类的完整的例子307.3类的初始化内部类型或结构可以方便的初始化,例如int year=2001;struct thing char * pn; int m;thing something=“Beijing”, 23.5;/以下的初始化会出错Stock kate=“Sukie, Inc. ”, 200, 33.56;/原因是类的数据部分访问是私有的,程序不可直接访 /数据成员31构造函数类初始化机制是构造函数,

21、它保证在每个对象的首次使用之前被编译器自动应用在每个类对象上-准确地说,C+提供了名称和使用句法,程序员提供方法的定义构造函数与类同名,以此来标识构造函数构造函数不能指定返回类型class Account public:/ 缺省构造函数Account();/ .private:char *_name;unsigned int _acct_nmbr;double _balance;32C+语言对于一个类可以声明多少个构造函数没有限制,只要每个构造函数的参数表惟一的即可在最小情况下,必须允许用户为每一个需要设置的数据成员提供一个初始值。例如一个账户号码要能被设置或自动生成来保证其惟一性Accoun

22、t( const char *name, double open_balance );用户可能只要求指定一个名字,让构造函数自动把_balance 初始化为0Account( const char *name );Account( const char *name, double open_balance = 0.0 );33Account类定义可能具有如下的结构:class Account public:/ 缺省构造函数Account();/ 声明中的参数名不是必需的Account( const char*, double=0.0 );const char* name() return _n

23、ame; / .private:/ .;合法的Account 类对象定义int main()/ ok: 都调用双参数构造函数Account acct( Ethan Stern );Account *pact = new Account( Michael Lieberman, 5000 );if ( strcmp( (), pact-name() )/ .34在类对象首次被使用之前,构造函数将被应用在该对象上为构造函数指定实参有三种等价形式/ 一般等价的形式Account acct1( Anna Press );Account acct2 = Account( Anna Press );Acco

24、unt acct3 = Anna Press;acct3 的形式只能被用于指定单个实参的情形一般来说,推荐使用acct1 的形式下面为Stock类定义一个构造函数35 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:

25、cerr Number of shares cant be negative; company shares set to 0.n; shares = 0; else shares = n; share_val = pr; set_tot();36注意:构造函数声明中的参数名可以不写;构造函数定义中的参数名不能使用类成员名,例如class Stockpublic:Stock(const char * co, int n, double pr);/private:char co 30; int n; double pr;/错误:会出现n=n ; pr=pr ; 这样的赋值 Stock:Stock

26、(const char * co, int n, double pr)37缺省构造函数缺省构造函数是指不需要用户指定实参就能够被调用的构造函数。即它用于如下形式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(c

27、ompany, no name); shares = 0; share_val = 0.0; total_val = 0.0;38有了缺省构造函数之后就可以用以下的方式创建类对象Stock first;Stock second = Stock( );Stock * pstock = new Stock;要注意区别下列的形式:Stock first (“Sukie, Inc. ”);Stock second ( );Stock third;39成员初始化表类的初始化有一种可替换的语法:成员初始化表, 是由逗号分开的成员名及其初值的列表/ Account缺省构造函数inline Account:A

28、ccount() _name = 0;_balance = 0.0;_acct_nmbr = 0;可以由如下的成员初始化表替代40/ 使用成员初始化表的缺省 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

29、 = new char strlen(name)+1 ; strcpy( _name, name ); _acct_nmbr = get_unique_acct_nmbr();41拷贝构造函数用一个类对象初始化该类的另一个对象被称为缺省按成员初始化一个类对象向该类的另一个对象作拷贝,是通过依次拷贝每个非静态数据成员来实现的如果定义了拷贝构造函数,则在用一个类对象初始化该类另一个对象时它就会被调用,来替代缺省的行为缺省按成员的初始化对于类的正确行为常常是不合适的拷贝构造函数有一个指向类对象的引用作为形式参数,传统上被声明为const42inline Account:Account( const

30、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();437.4 析构函数构造函数的目的之一是为了自动获取资源,如通过应用new 表达式分配了一个字符数组;析构函数是一种对称的操作,它为生命期即将结束的类对象返还相关的资源或者自动释放资源析构函数的名字是在类名前加上波浪线 ,它不返回任何值也没有任何参数不能被重载Inli

31、ne Account:Account() delete _name; return_acct_nmbr( _acct_nmbr );44析构函数的自动执行时间:静态存储类对象,程序结束时自动存储对象,定义对象的代码块执行完毕时用new创建的对象,在使用delete收回内存时45析构函数并不是必需的考虑下面的类class Point3d public: / . private: float x, y, z;一般地,如果一个类的数据成员是按值存储的,则无需析构函数46在Stock中,析构函数不需要做什么事情,可以写成这样来验证一下/ class destructorStock:Stock() /

32、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 (s.topvaltopval) return s; else return *this;50注意:每个成员函数(包括构造函数和析构函数)中都包含一个this指针该指针指向了成员函数的调用者(类对象)。同一个类创建多

33、个对象,每个对象在调用成员函数时,this都指向该调用对象this指针的使用方法this-m_member(*this). m_member加入了成员函数topval的类定义和其实现见文件stock2.h, stock2.cpp517.6 对象数组可以用类类型创建数组,就像使用内置数据类型一样:Stock myarr3;声明了对象数组以后可以访问其成员函数:myarr0.update();myarr1.show( );Stock top1= myarr2.topval(myarr1);可见,对对象数组元素的使用,与使用类对象的方法是一样的。521 如果类中没有声明任何构造函数,使用如下方式:c

34、lass Aprivate: int x;A arr3;此时创建的对象没有被初始化。对象数组的创建方式532 如果声明了带参数的构造函数,但没有声明缺省构造函数,则使用第一种方法会出错,而应该使用构造函数初始化元素:class Aprivate: int x; double y;public: A (int a, double b);A arr3; /出错,这与创建单个对象是一致的54A arr3 = A(10, 12.5), /以合法的构造函数初始化 A(32, 10.87), A(5, 19.09);此时,分别按顺序初始化了3个数组元素。注意,必须为每个元素初始化。3 如果类中有多个构造函

35、数,但是没有缺省构造函数,则必须为每个元素初始化,可以使用不同的构造函数:55class 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();564 如果类中有构造函数,并且定义了缺省构造函数,则可以不初始化数组元素,或只初始化一部分元素class Aprivate: int x; d

36、ouble 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;57int 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.cpp58实现变更与接口变更Stock类仍有不足之处。例如股票的名称被限制为最多29个字符,

37、可以将私有成员的类型由字符数组改为string类型。这需要3处改动:头文件中加入包含类定义中构造函数的定义这种修改属于实现变更,不会影响到类的使用者。59还有一种变更是接口变更,这会影响到类的使用者。例如增加一个构造函数:Stock (const string & co, int n, double pr); 可供用户使用的方法增多了,例如sting co_name;getline( cin, co_name);Stock newstock (co_name, 100, 13.5);60类作用域在类中声明的名称作用域为整个类,即包括类声明和成员函数的定义区域,称为类作用域。所以在类声明或成员函数定义中可以直接使用类中声明的标识符在其它情况下,要使用成员名称必须加以修饰。共有3种修饰符:直接成员操作符,间接成员操作符和作用域解析操作符唯一例外是构造函数的使用,因为其名字和类名是一样的Stock mystock = Stock

温馨提示

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

评论

0/150

提交评论