码迷,mamicode.com
首页 > 编程语言 > 详细

C++面向对象程序设计(陈维兴 林小茶)精讲

时间:2019-01-01 23:57:26      阅读:324      评论:0      收藏:0      [点我收藏+]

标签:管理   虚拟   行操作   功能   union   形式   void   读取   整型   

面向对象设计主要特征是程序=对象+消息,对象是基本元素,对象接收到消息后,启动有关方法完成操作。

面向对象程序设计的基本特征有:抽象、封装、继承和多态。

c++支持编译时的多态和运行时的多态,编译时的多态通过函数重载实现,运行时的多态通过虚函数实现。

c++通过对c进行扩充,是面向过程程序设计和面向对象设计的混合型程序设计语言。

c++程序一般由类的声明和类的使用两大部分组成。

c++对c的扩充:

l  c++除了保留c的进行输入/输出操作时常使用的printf()和scanf()函数外,新增标准输入流对象cin和标准输出对象cout。cin遇到空格,就认为本数据输入结束并检查输入数据与变量的匹配情况。c++增加了操纵符对输出数据格式进行控制,如换行操纵符endl。

l  在c语言中全局变量必须声明在任何函数之前,局部变量必须集中在可执行语句之前,而c++允许变量声明可与可执行语句交替出现。

l  在c++中,结构名、联合名、枚举名都是类型名。可以像定义对象那样,定义结构体、联合体、枚举。不必在前面冠以struct、union、enum。

l  c语言建议为每一个函数建立函数原型,c++必须建立函数原型,以便在编译时进行类型检查。

l  c语言常用#define 来定义常量,但是容易出错,c++推荐用const修饰符。

l  内联函数以空间换时间的方式降低函数调用时的系统开销,但要求内联函数体内不含有负杂的控制语句,也不能过长,免得程序编译后体量过大。c++中一个函数在类体内则被隐式的指定为内联函数,也可以用inline显式的指定内联函数。

l  可以定义带有默认参数的函数。

l  用户可以重载函数,即在同一作用域中,可以定义函数名相同的函数,只要函数参数类型不同或者参数个数不同或者兼而有之。

l  作用域标识符::c语言中函数内如有和全局变量同名的局部变量,则全局变量会被屏蔽无法访问,而c++中可以在变量名前面使用::表明是使用全局变量。

l  计算机内存会被分为四个区:程序代码区、全程数据区、栈、堆。只有堆可由用户分配和释放。c语言中使用函数malloc函数和free来动态管理内存,c++提供了运算符new、delete来做同样的工作。

l  引用,c++中引用就是变量的别名,故又称别名。引用和原变量名指向内存中的同一区域。

l  传递函数参数的三种情况,1、将变量名作为函数参数,这是“传值调用”,是单向传递,在函数运行过程中,形参的变化不会传递给实参,2、指针变量作为函数参数,这是“传址调用”,是双向传递,3、引用作为函数参数,也是“传址调用”双向传递。

l  通常一个函数不能直接用在赋值运算符的左边,例如

index(2)=25;这是不允许的,但是使用引用返回函数值就可以这么写了:

int& index(int i)

{

 return a[i];

}

index(2)=25;这是允许的,相当于a[2]=25;要注意的是引用不是一种数据类型,所以不能定义引用的引用、引用的指针、引用的数组。

 

类的构成:类和结构体的扩种形式十分相似,类生命中包括数据和函数,分别称为数据成员和成员函数,数据成员和成员函数有公有、保护、私有三种访问权限。一般情况下,类体中只给出成员函数原型,而函数体的定义放在类外。需要注意的是不能在类声明中给数据成员赋初值。

相同类型的对象可以相互赋值,如a=b,对象之间的赋值,仅仅是对数据成员的赋值,不同的对象的数据成员占据不同的存储空间而不同对象的成员函数是占有同一个函数代码段,无法对他们赋值。当类体中存在指针时,使用赋值运算符进行赋值,可能会产生问题,即所谓的“浅拷贝”和“深拷贝”问题,浅拷贝时,因为含有指针,两个对象指针指向同一块内存区域,同一个对象调用析构函数时,该块内存被释放,当另一个也调用析构函数,同样要释放该块内存,而同一块内存不能被释放两次,这时便会出现问题,要解决该类问题,就需要使用“深拷贝”。

构造函数是特殊的成员函数,其函数名必须和类名相同,可以有任意的参数但不能有返回值甚至void也不行,它不需要用户调用,在定义对象时自动执行且只执行一次,为对象分配空间,进行初始化。

c++提供了除赋值运算符之外的初始化数据成员的方法,即成员初始化列表。对于用const修饰的数据成员,或是引用类型的数据成员,是不允许用赋值语句直接赋值的,只能用成员初始化列表进行初始化。
析构函数是另一种特殊成员函数,通常用于撤销对象时的一些清理工作,如释放分配给对象的内存空间等。析构函数和构造函数名字相同,但在前面加一个波浪号(~),析构函数没有参数也没有返回值,而且不能重载。因此一个类中只有一个析构函数。当撤销对象时,编译系统会自动调用析构函数。

拷贝构造函数,它的作用是在建立一个新对象时,使用一个已经存在的对象去初始化这个新对象,如point p1(p2)或point p1=p2;拷贝构造函数只有一个参数,并且是同类对象的引用,每个类都必须有一个拷贝构造函数。

因为成员函数代码是同类所有对象共用,为了使成员函数辨别出当前调用自己的是哪个对象,c++引入了自引用指针this,每当创建一个对象,系统就把this指针指向该对象,当调用成员函数时,系统把this指针作为一个隐含参数传递给该函数。

对象中的数据成员有自己独立的存储空间,互不相干,但有时希望不同的对象可以共享一个或几个数据成员,实现同类的不同对象间数据共享,于是有了静态成员的概念。无论建立多少类的对象,都只有一份静态数据成员的拷贝,从而实现同类的不同对象间的数据共享,静态成员属于类,所以可以用类直接访问。静态成员函数不是为了对象间的沟通,而是为了处理静态数据成员。静态成员函数没有this指针,所以一般不访问非静态成员,如果要访问非静态成员,则需显式的通过对象名.非静态成员名来访问。

有了静态成员来实现同类不同对象间的数据共享,同样为了实现类间的数据共享,c++引入了友元这一概念。友元是一扇通向私有成员的后门。

一个类的友元函数不是该类的成员,它可以是不属于任何类的非成员函数,也可以是另一个类的成员函数。定义时需在友元函数前添加关键字friend。一个类的友元函数可以访问该类的私有成员,但它毕竟不是该类成员,所以在定义和调用该类时不必像成员函数那样在函数名前加上“类名::”。它也没有this指针,需要显式的通过“对象名.数据成员”才能访问数据成员。成员函数只能访问它所属的类,但一个函数如果被定义为多个类的友元函数,那它可以访问这些类的所有数据。使用友元函数时必须要慎重。

可以将一个类比如y定义为另一个类x的友元类,那么y类中的所有成员函数都是x类的友元函数。

继承就是从先辈处得到属性和行为特征,较好的解决了代码重用的问题。默认为私有继承,还有公有继承、保护继承。基类的构造函数和析构函数不能被继承,当创建派生类对象时,先调用基类的构造函数,在调用派生类的构造函数,撤销派生类的对象时,顺序相反。在没有虚函数的情况下,如果派生类中定义了与基类成员同名的成员,称派生类成员覆盖了基类成员,如要在派生类中使用基类同名成员,必须使用“基类名::同名成员”的方式显式指定,如果在对象中调用就用“对象名.基类名::同名成员”。

当一个派生类有多个基类时,成为多继承。多继承要注意构造函数的书写,同时多继承带来一个问题:当派生类y有两个基类a和b,同时a和b有一个共同的基类c,这样y就会继承两个c的拷贝,当要访问c的成员时,必须显式的指定是a还是b的成员,以免产生二义性,为了解决这个问题,c++引入虚基类的概念。上例中可以把c声明为a和b的虚基类,即:class a:virtual 继承方式 c和class b:virtual 继承方式 c这样从a和b继承的y只会继承c一次。

不同数据类型数据之间的自动转换和赋值,称为赋值兼容。基类和派生类对象之间也存有赋值兼容关系:在基类对象可以使用的地方都可以用派生类的对象来替代。派生类对象可以赋值给基类对象,指向基类对象的指针可以指向基类的公有派生类,但不能指向私有派生类。

所谓多态性就是不同对象收到相同的消息,产生不同的动作。连编是把函数名和函数体的程序代码连接(联系)在一起的过程。静态连编在编译阶段就完成了,函数调用速度快,效率高,但缺乏灵活性,动态连编在运行阶段完成,在程序运行时才去确定调用哪个函数,降低了程序运行效率但增强了灵活性。c++是编译型语言,仍采用静态连编,好在c++引入了“虚函数”从而实现了静态连编和动态连编相结合。虚函数是基类中的成员函数,前面加有关键字virtual,并在派生类中被重载,在派生类中被重新定义时其函数原型包括返回类型、函数名、参数个数、参数类型的顺序,都必须和基类中的原型完全相同。

虚函数使用的基础是赋值兼容规则,而赋值兼容规则成立的前提条件是派生类从其基类公有派生,所以要想通过定义虚函数来实现动态多态性派生类就必须是从基类公有派生。内联函数不能是虚函数,因为内联函数不能在运行中动态确定其位置,所以即使虚函数在类的内部定义。编译时仍将它看成非内联的。

基类往往表示一种抽象的概念,此时在基类中预留一个函数名,具体功能留给派生类根据需要去定义,这样一个虚函数就成为纯虚函数,格式如下:virtual 返回类型 函数名(参数表)=0;含有纯虚函数的类称为抽象类。抽象类的目的就是用它去建立派生类,抽象类不能实例化为对象。也不允许从非抽象类派生出抽象类,抽象类也不能做函数参数类型、返回类型或显式转换的类型。

运算符的重载,需要写一个运算符函数,比如要重载”+”号,就要写一个名为operator+的函数。运算符重载函数有两种形式,一种是定义为它要操作的类的成员函数,另一种是定义为该类的友元函数。友元运算符重载函数如下:

class x{

……

friend 返回类型 operator运算符(形参表);

……

}

注意友元函数不是类x的成员不能直接访问x的成员,要显式指定“x.成员名”也没有this指针。并不是所有的运算符都可以定义为友元运算符重载函数,如赋值运算符“=”,下标运算符“[]”,函数调用运算符“()”等。

对于x=x1+x2;c++解释为x=operator+(x1,x2);

成员运算符重载函数格式如下:

class x{

……

 返回类型 operator运算符(形参表);

……

}

 

因为成员函数可以在函数体内直接访问类x的成员,而且有this指针隐含的指向当前对象,所以对于双目运算符,只需一个形参就可以了。

对于x=x1+x2;c++解释为x=x1.operator+(x2);

一般来说,双目运算符可以被重载为友元函数活成员函数,但也有例外只能用友元函数,比如一个类的对象和一个整数或其他类对象相加的成员函数:

如果是x和整数i相加 x=x1+i;是正确的,因为c++解释为x=x1.operator+(i);

但x=i+x1;却出错,因为c++解释为x=i.operator+(x1);但是i是整数,并没有成员函数operator+所以编译出错。

为了解决这一问题,只能定义两个友元函数:

class x{

……

friend 返回类型 operator运算符(x& x1,int i);

friend 返回类型 operator运算符(int i, x& x1);

 

……

}

利用函数重载来解决运算符两边操作数交换的问题。

对于“++”和“--”等分前缀用法后缀用法的运算符用“int”区分,没有int是前缀用法,有是后缀用法。

class x{

……

friend 返回类型 operator运算符(x& x1);//前缀

friend 返回类型 operator运算符( x& x1,int);//后缀

 

……

}

为了解决“浅拷贝”带来的“指针悬挂”问题,可以重载赋值运算符”=”,引入深拷贝。

类型转换分为标准类型(如int,float,double,char等)间的转换和类类型和标准类型间的转换。准类型间的转换c++已经自带了方法转换,不需用户在写方法。

类类型和标准类型间的转换有两种方法1、通过转换构造函数进行类型转换2、通过类型转换函数进行类型转换。

在程序设计的过程中经常出现这样的情况:两个或多个函数的函数体完全相同,差别仅在于它们的参数类型不同,为了提高代码的可重用性和可维护性,c++提出了模板概念。

在c语言中可以用宏定义#define,但是宏定义避开了类型检查,容易出错。

模板可以实现参数类型参数化,即把数据类型定义为参数,从而实现代码重用。模板分为函数模板和类模板,他们分别用来建立模板函数和模板类。

函数模板是建立一个通用函数,其函数返回类型和参数类型不具体指定,用一个虚拟的类型来代表,这个通用函数就是函数模板,系统调用函数时根据实参的类型取代模板中虚拟类型。

格式如下:

template  <typename 类型参数>

返回类型 函数名(模板形参表)

{

函数体

}

或者

template <class 类型参数>

返回类型 函数名(模板形参表)

{

函数体

}

一般为了与类声明中的class区分,一般用第一种格式,c++中的类型参数一般是t、type等。,typename和class用来表名后面的参数是一个虚拟的类型名。函数模板需要实例化一个模板函数才能调用,当编译系统发现一个函数调用

函数名(模板实参表)

就会根据模板实参表中的类型生成一个函数即模板函数。模板函数中的函数体与函数模板函数体相同。函数模板可以和同名的非模板函数重载,调用顺序是先寻找一个参数完全匹配的非模板函数,如果有就调用没有就寻找函数模板将其实例化,如实例化模板函数成功就调用它。

对于类的声明也可以采用类似的方法,使用类模板可以简化那些功能相同而数据类型不同的类的声明。格式如下:

template <typename 类型参数>

class 类名{

类成员声明

};

template <class 类型参数>

class 类名{

类成员声明

};

类模板定义对象的格式是:

类名<数据实际类型> 对象名(参数表);

c++支持c语言的输入输出系统之外,还定义了一套面向对象的输入输出系统。“流”是数据从一个源留到目的的抽象,负责数据的生产者和消费者之间建立联系并管理数据的流动。i/o流类库中各种类的声明包含在相应的头文件中如iostream、fstream、strstream、iomanip。

类ios是流的基类是抽象类。流类定义的对象称为流对象,c++中有几个预定义好的流对象:标准输入流对象cin、标准输出流对象cout、非缓冲型标准出错流对象cerr和缓冲型标准出错流对象clog。

cout中常用的成员函数:cout.put(a)输出一个字符a;

cin中常用的成员函数:cin.get(ch)功能是从输入流读取一个字符(包括空白符)赋给字符型变量ch;cin.getline(字符数组,字符个数n,终止标识符)或cin.getline(字符指针,字符个数n,终止标识符)功能是从输入流读取n-1个字符,如果提前读到终止标识符就提前结束最后总要插入一个字符串结束标志’\n’。cin.ignore(n,终止字符)功能是跳过输入流中的n个(默认1个)字符或遇到指定的终止字符(默认是eof)提前结束跳跃。

流基类ios中定义了一些进行输入输出格式控制的成员函数,查看书籍,除此之外c++提供了另一种输入输出格式控制的方法,称为操纵符,查阅书籍,用户也可以自定义操纵符。格式如下:

输出流:

ostream &操纵符名 (ostream &stream)

{

自定义代码

return stream;

}

输入流:

istream &操纵符名 (istream &stream)

{

自定义代码

return stream;

}

文件流用来处理外存文件,根据文件中数据的组织形式,文件可分为两类:文本文件和二进制文件。文本文件又称ASCii文件,一个字节存放一个ASCII代码代表一个字符,二进制文件则是内存中的存储形式原样写到外存中形成的文件,比如整数100,在文本文件中是以‘1’、‘0’、‘0’三个字符的ASCII的代码存放的,占3个字节,而在二进制文件中就是100的二进制形式01100100存放的,占一个字节。

C++进行文件操作的一般步骤:1、为要进行操作的文件建立一个文件流对象,2、打开文件,如果不存在就创建该文件,3、进行读写操作,4、关闭文件。用到的类ifsteam(用于文件输入)、ofsteam(文件输出)、fsteam(文件输入输出)。

成员函数:

文件流对象.open(文件名,使用方式):以特定方式打开文件

文件流对象.open():关闭文件

文件流对象<<数据:写入文件

文件流对象>>变量:读取数据

文件流对象.read(char  *buf,int len):读取len个字符到buf数组

文件流对象.write(const char  *buf,int len):将buf数组中len个字符写入文件。

文件流对象.eof():检测是否到达文件尾。

随机读写函数:看书籍。

命名空间是由程序设计者命名的一个内存区域,用来处理程序中同名冲突问题。定义格式如下:

Namespace 空间名

{

代码

}

C语言中没有命名空间,如果C++使用的带扩展名.h的头文件,不必使用命名空间。如果C++使用的不带扩展名.h的头文件就要指定头文件所在的命名空间。

程序中的常见错误分为:编译时的错误和运行时的错误。编译时的错误主要是语法错误,运行过程的错误统称为异常,对异常的处理就是异常处理。

C++中处理异常的办法是:如果执行一个函数出现异常,可以不在本函数处理而是甩给上一级也就是函数的调用者,一直可以逐级上传一直到最高级,如果最高级也无法处理,系统会调用系统函数terminate(),有它调用abort终止程序。

C++异常处理机制有检查、抛出和捕获三个部分:try(检查)、catch(捕获)、throw(抛出)

格式如下:

Throw 表达式:

Int  i;

Throw  i;因为i是整型变量,所以throw抛出的是整型异常;

Throw抛出的异常会由catch捕获。

Try

{

被检查的语句

}

Catch(异常类型声明1)

{

进行异常处理的语句1

}

Catch(异常类型声明2)

{

进行异常处理的语句2

}

……

Try和catch必须配套使用。

本书主要内容就是这些。

 

C++面向对象程序设计(陈维兴 林小茶)精讲

标签:管理   虚拟   行操作   功能   union   形式   void   读取   整型   

原文地址:https://www.cnblogs.com/heart-flying/p/10206312.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!