c++面向对象语法总结

来源:互联网 发布:淘宝怎么装修店铺 编辑:程序博客网 时间:2024/06/10 09:21


/* 类和对象 */



1. 对象

客观世界中任何一个事物都可以看成一个对象(object)。对象可大可小。
对象是构成系统的基本单位。
任 何 一 个 对 象 都 应 当 具 有 这 两 个 要 素 , 即 属 性 (attribute) 和 行 为
(behavior),它能根据外界给的信息进行相应的操作。




2. 封装与信息隐蔽

可以对一个对象进行封装处理,把它的一部分属性和功能对外界屏蔽,
也就是说从外界是看不到的,甚至是不可知的。这样做的好处是大大降低了操
作对象的复杂程度。


封装,指两方面的含义:
一是将有关的数据和操作代码封装在一个对象中,形成一个基本单位,
各个对象之间相对独立,互不干扰。
二是将对象中某些部分对外隐蔽,即隐蔽其内部细节,只留下少量接口,
以便与外界联系,接收外界的消息。这种对外界隐蔽的做法称为信息隐蔽


3.抽象

在程序设计方法中, 常用到抽象(abstraction)这一名词。
 抽象的过程是将有关事物的共性归纳、集中的过程。抽象的作用是表示同一类事物的本质。


C和C++中的数据类型就是对一批具体的数的抽象。对象是具体存在的,
如一个三角形可以作为一个对象, 10个不同尺寸的三角形是10个对象。如果
这10个三角形对象有相同的属性和行为,可以将它们抽象为一种类型,称为三角形类型。


在C++中,这种类型就称为“类(class )”。这10个三角形就是属于同一“类”
的对象。类是对象的抽象,而对象则是类的特例,或者说是类的具体表现形
式。




4. 继承与重用

如果在软件开发中已经建立了一个名为A的“类”,又想另外建立一个名为B的“类”,
而后者与前者内容基本相同,只是在前者的基础上增加一些属性和行为,
只需在类A的基础上增加一些新内容即可。这就是面向对象程序设计中的继承机制。


利用继承可以简化程序设计的步骤。
“白马”继承了“马”的基本特征, 又增加了新的特征(颜色), “马”是父类,
或称为基类, “白马”是从“马”派生出来的,称为子类或派生类。




5.多态性

如果有几个相似而不完全相同的对象,有时人们要求在向它们发出同一个消息时,
它们的反应各不相同, 分别执行不同的操作。这种情况就是多态现象。


如,在Windows环境下,用鼠标双击一个文件对象(这就是向对象传送一个消息),
如果对象是一个可执行文件,则会执行此程序,如果对象是一个文本文件,则启动文本编辑器并打开该文件。


在C++中, 所谓多态性(polymorphism)是指: 由继承而产生的相关的不同的类,
其对象对同一消息会作出不同的响应。多态性是面向对象程序设计的一个重要特征,能增加程序的灵活性。




基于对象和面向对象程序设
计就是把一个算法和一组数据结构封装在一个对象中。因此,就形成了新的
观念:
对象 = 算法 + 数据结构
程序 = (对象+对象+对象+…) + 消息
或:
程序 = 对象s + 消息




class定义的类,如果不作private或public声明,系统将其成员默认为 private


每个对象所占用的存储空间只是该对象的数据部分所占用的存储空间
用一段空间来存放这个共同的函数代码段




/*有关构造函数的使用,有以下说明:*/

(1) 在类对象进入其作用域时调用构造函数。
(2)构造函数没有返回值,因此也不需要在定义构造函数时声明类型,这
是它和一般函数的一个重要的不同之点。
(3) 构造函数不需用户调用,也不能被用户调用。
(4) 在构造函数的函数体中不仅可以对数据成员赋初值,而且可以包含
其他语句。
但是一般不提倡在构造函数中加入与初始化无关的内容,以保持程序的
清晰。
(5) 如果用户自己没有定义构造函数,则C++系统会自动生成一个构造函
数,只是这个构造函数的函数体是空的,也没有参数,不执行初始化操作




/*~析构函数*/

①如果在一个函数中定义了一个对象(它是自动局部对象),当这个函数
被调用结束时,对象应该释放,在对象释放前自动执行析构函数。
②static局部对象在函数调用结束时对象并不释放,因此也不调用析构函
数,只在main函数结束或调用exit函数结束程序时,才调用static局部对象的析
构函数。
③如果定义了一个全局对象, 则在程序的流程离开其作用域时(如main
函数结束或调用exit函数) 时,调用该全局对象的析构函数。
④如果用new运算符动态地建立了一个对象,当用delete运算符释放该对
象时,先调用该对象的析构函数。




调用析构函数的次序正好与调用构造函数的次序相反 :
最先被调用的构造函数, 其对应的(同一对象中的)析构函数最后被调用,而最
后被调用的构造函数,其对应的析构函数最先被调用。






下面归纳一下什么时候调用构造函数和析构函数 :


(1) 在全局范围中定义的对象(即在所有函数之外定义的对象),它的构造
函数在文件中的所有函数(包括main函数)执行之前调用。
但如果一个程序中有多个文件,而不同的文件中都定义了全局对象,则
这些对象的构造函数的执行顺序是不确定的。
当main函数执行完毕或调用exit函数时(此时程序终止),调用析构函数。(2) 如果定义的是局部自动对象(例如在函数中定义对象),则在建立对象
时调用其构造函数。
如果函数被多次调用,则在每次建立对象时都要调用构造函数。
在函数调用结束、对象释放时先调用析构函数。
(3) 如果在函数中定义静态(static )局部对象,则只在程序第一次调用此
函数建立对象时调用构造函数一次,在调用结束时对象并不释放,因此也不
调用析构函数,只在main函数结束或调用exit函数结束程序时,才调用析构函数。






/*常对象。*/

定义常对象的一般形式为
类名 const 对象名[(实参表列)];
也可以把const写在最左面:
const 类名 对象名[(实参表列)];
二者等价。
如果一个对象被声明为常对象, 则不能调用该对象的非const型的成员函
数(除了由系统自动调用的隐式的构造函数和析构函数)。


引用常对象中的数据成员很简单,只需将该成员函数声明为const即可。如 :
void get_time( ) const ; //将函数声明为const


指向对象的常指针
Time * const ptr1=&t1; //指定ptr1指向t1


指向常对象的指针变量
ptr: const char *ptr;


(1) 如果一个变量已被声明为常变量,只能用指向常变量的指针变量指
向它, 而不能用一般的(指向非const型变量的)指针变量去指向它。
(2) 指向常变量的指针变量除了可以指向常变量外,还可以指向未被声
明为const的变量。
此时不能通过此指针变量改变该变量的值。
如果希望在任何情况下都不能改变c1的值,则应把它定义为const型。
(3) 如果函数的形参是指向非const型变量的指针,实参只能用指向非
const变量的指针,而不能用指向const变量的指针,这样,在执行函数的过程
中可以改变形参指针变量所指向的变量(也就是实参指针所指向的变量)的值。




/* 类的继承 */

类的继承是用已有的类来建立专用类的编程技术。


一个派生类不仅可以从一个基类派生,也可以从多个基类派生。
一个派生类有两个或多个基类的称为多重继承(multiple inheritance)


基类和派生类的关系,可以表述为:派生类是基类的具体化,而基类
则是派生类的抽象。


class 派生类名: [继承方式] 基类名{派生类新增加的成员};
继承方式包括: public (公用的),private (私有的)和protected(受保护的)




构造一个派生类包括以下3部分工作:
(1) 从基类接收成员。
(2) 调整从基类接收的成员。
(3) 在声明派生类时增加的成员。


一般还应当自己定义派生类的构造函数和析构
函数,因为构造函数和析构函数是不能从基类继承的。


派生类是抽象基类的具体实现。


( 1) 公用继承(public inheritance)
基类的公用成员和保护成员在派生类中保持原有访问属性,其私有成员
仍为基类私有。
( 2) 私有继承(private inheritance)
基类的公用成员和保护成员在派生类中成了私有成员。
其私有成员仍为基类私有。
( 3) 受保护的继承(protected inheritance)
基类的公用成员和保护成员在派生类中成了保护成员,其私有成员仍为
基类私有。
保护成员的意思是: 不能被外界引用,但可以被派生类的成员引用,具
体的用法将在稍后介绍。




派生类构造函数名(总参数表列) : 基类构造函数名(参数表列) {派生
类中新增数据成员初始化语句}
( 1) 对基类数据成员初始化;
( 2) 对子对象数据成员初始化;
( 3) 对派生类数据成员初始化
派生类构造函数名(总参数表列) : 基类构造函数名(参数表列),子
对象名(参数表列){派生类中新增数成员据成员初始化语句}
① 调用基类构造函数,对基类数据成员初始化;
② 调用子对象构造函数,对子对象数据成员初始化;
③ 再执行派生类构造函数本身,对派生类数据成员初始化。


派生类的析构函数
派生类是不能继承基类的析构函数的,也需要通过派生类的
析构函数去调用基类的析构函数。
基类的清理工作仍然由基类的析构函数负责。
调用的顺序与构造函数正好相反




派生类同时继承多个基类, 这种行为
称为多重继承(multiple inheritance)。
派生类构造函数名(总参数表列):基类1构造函数(参数表列),基
类2构造函数(参数表列),基类3构造函数(参数表列){派生类中新增
数成员据成员初始化语句}




/*虚基类*/

C++提供虚基类(virtual base class )的方法,使得在继承间接共同基类时只保留一份成员。
class 派生类名: virtual 继承方式 基类名
需要注意: 为了保证虚基类在派生类中只继承一次, 应当在该基类的所有直接派生类中声明为虚基类。


许多专业人员认为:不要提倡在程序中使用多重继承,只有在比较简单和不易出现二义性的情况
或实在必要时才使用多重继承,能用单一继承解决的问题就不要使用多重继承。


也是由于这个原因, 有些面向对象的程序设计语言(如Java, Smalltalk)并不支持多重继承。


/*类的组合*/
在一个类中以另一个类的对象作为数据成员的,称为类的组合(composition)。
类的组合和继承一样,是软件重用的重要方式。




/*多态性*/

在面向对象方法中一般是这样表述多态性的:向不同的对象发送同一个消息,
不同的对象在接收时会产生不同的行为(即方法)。也就是说, 每个对象可以用自己的方式去响应共同。


静态多态性是通过函数的重载实现的
动态多态性是通过虚函数


与基类成员同名的成员在不同的派生类中有不同的含义。也可以说, 多态性是“一个接口,多种方法”。


编译系统按照同名覆盖的原则决定调用的对象。如果想调用被覆盖成员需加域名 ::

虚函数的作用是允许在派生类中重新定义与基类同名的函数,(动态性 运行时生成)
并且可以通过基类指针或引用来访问基类和派生类中的同名函数。


虚函数的使用方法是:
(1)在基类用virtual声明成员函数为虚函数。
这样就可以在派生类中重新定义此函数,为它赋予新的功能,并能方便地被调用。
在类外定义虚函数时,不必再加virtual。
(2)在派生类中重新定义此函数,要求函数名、函数类型、函数参数个数
和类型全部与基类的虚函数相同,并根据派生类的需要重新定义函数体。
(3)定义一个指向基类对象的指针变量,并使它指向同一类族中需要调用
该函数的对象。
(4)通过该指针变量调用此虚函数,此时调用的就是指针变量指向的对象
的同名函数。
通过虚函数与指向基类对象的指针变量的配合使用,就能方便地调用同
一类族中不同类的同名函数,只要先用基类指针指向即可。


当一个类带有虚函数时, 编译系统会为该类构造一个虚函数表(virtualfunction table,简称vtable), 
它是一个指针数组,存放每个虚函数的入口地址。系统在进行动态关联时的时间开销是很少的,故多态性是高效的。




/**虚析构函数**/

析构函数的作用是在对象撤销之前做必要的“清理现场”的工作。
用new运算符建立了临时对象 delete运算符撤销对象
会发生一个情况:系统会只执行基类的析构函数,而不执行派生类的析构函数.


当基类的析构函数为虚函数时,无论指针指的是同一类族中的哪一个类对象,系统会采用动态关联,
如果希望能执行派生类Circle的析构函数,可以将基类的析构函数声明为虚析构函数,
最好把基类的析构函数声明为虚函数。这将使所有派生类的析构函数自动成为虚函数。


专业人员一般都习惯声明虚析构函数,即使基类并不需要析构函数,也显式地定义一个函数体为空的虚析构函数,
以保证在撤销动态分配空间时能得到正确的处理。




/**纯虚函数**/

只给出函数的原型,并在后面加上“=0”,如
virtual float area( )const =0;//纯虚函数
这就将area声明为一个纯虚函数(pure virtual function)。
纯虚函数是在声明虚函数时被“初始化”为0的函数。


①纯虚函数没有函数体 ;
②最后面的“=0”并不表示函数返回值为0,它只起形式上的作用,告诉编译系统“这是纯虚函数” ;
③这是一个声明语句,最后应有分号 ;


它只是通知编译系统:“在这里声明一个虚函数,留待派生类中定义”。
派生类中没有对该函数定义,则该虚函数在派生类中仍然为纯虚函数。




/**抽象类**/
不用来定义对象而只作为一种基本类型用作继承的类,称为抽象类(abstract class ),
由于它常用作基类, 通常称为抽象基类(abstract base class )。凡是包含纯虚函数的类都是抽象类。


因为纯虚函数是不能被调用的,包含纯虚函数的类是无法建立对象的。抽象类的作用
是作为一个类族的共同基类,或者说,为一个类族提供一个公共接口。
0 0