C++复习资料.docx
- 文档编号:6408813
- 上传时间:2023-01-06
- 格式:DOCX
- 页数:28
- 大小:32.41KB
C++复习资料.docx
《C++复习资料.docx》由会员分享,可在线阅读,更多相关《C++复习资料.docx(28页珍藏版)》请在冰豆网上搜索。
C++复习资料
第一章类和对象
类和对象是面向对象程序设计的两个最基本、最重要的概念。
所谓对象就是客观事务在计算机中的抽象描述;而所谓类则是对具有相似属性和行为的一组对象的统一描述。
从程序设计语言的角度来说,类是一种数据类型,而对象是具有这种类型的变量。
1.类的定义
类定义的一般形式为:
class类名
{
private:
数据成员或成员函数
protected:
数据成员或成员函数
public:
数据成员或成员函数
};
<各成员函数的实现代码>
其中,class是定义类的关键字。
类名是一个有效的标志符,且一般首字母大写。
大括号括起来的部分是类说明部分,它声明了类的所有成员(包括数据成员和函数成员),这些成员从访问权限上分成三类,即私有(private)、公有(public)和保护(protected),其中默认权限为private。
类的private部分说明的成员,在类之外是不能存取的,只有类中的成员函数才能存取private的数据成员和成员函数。
类的public部分说明的成员,可被程序中的任何函数或语句存取,public成员多为成员函数,用来提供一个与外界的接口,外界只有通过这个接口才可以实现对private成员的存取。
类的protected部分说明的成员,不能在类之外存取,只有类的成员函数及其子类(派生类)可以存取protected的成员。
2.成员函数的定义
类中的成员函数可以在两处定义:
一是将成员函数的定义直接写在类中,一般适合于成员函数规模较小的情况;二是在类的定义体中只写出成员函数的原型说明,而成员函数的定义写在类的定义之外,这种情况比较适合于成员函数体较大的情况,其定义格式为:
返回值类型类名:
:
成员函数名(参数说明)
{
函数体;
}
此处的“:
:
”符号称为作用域运算法(名空间分隔符),它是用来指明哪个函数属于哪个类或哪个数据属于哪个类。
需要说明的是,成员函数既可以是有参函数,也可以像普通函数一样,给形参指定默认值。
3.对象及指向对象的指针
对象是类的实例,定义对象之前一定要说明该对象的类。
定义对象的一般格式为:
类名对象名表;
其中,对象名表中可以有一个或多个对象名,多个对象名之间用逗号分隔。
另外,也可以定义指向类类型的指针,其定义格式为:
类名*指针变量名;
4.访问对象的成员
访问对象的成员有以下两种格式:
(1)对象名.成员名
(2)指针变量名->成员名
第一种格式是利用对象和运算符“.”访问成员;而第二种格式是采用指向对象的指针和运算符“->”来访问成员,并且第二种格式中的指针变量必须已指向某个对象。
5.类成员指针
在C++语言中,除了可以定义指针对象外,还可以定义类成员指针。
类数据成员指针就是程序中定义的用于指向类中数据成员的指针变量,借助该指针变量可以访问它所指向的类中的数据成员(该数据成员必须具有public访问权限),但该指针变量不是类的成员,它只是程序中的一个指针变量而已。
类数据成员指针的定义格式如下:
类型类名:
:
*指针变量名;
此处的“类型”要与类中数据成员的类型保持一致。
要使已经定义的类数据成员的指针变量指向类中某个数据成员时,可以通过以下语句:
类数据成员指针变量名=&类名:
:
类数据成员变量名;
当类数据成员指针变量已经指向类中某个数据成员时,可以通过以下语句访问类中数据成员:
对象名.*类数据成员指针变量名;
6.this指针
在每一个类成员函数的形参表中都有一个隐含的指针变量this,该指针变量的类型就是成员函数所属类的类型。
当程序中调用成员函数时,this指针变量被自动初始化为发出函数调用的对象的地址。
尽管我们在定义成员函数时没有看到this指针变量,也没有定义this指针变量,但是在成员函数的函数体内我们可以使用this指针变量,因为该指针变量使系统隐含给出的,我们不需要也不能在成员函数的形参表中对this指针变量进行显示说明。
类中成员函数有了隐含的指针变量this后,就可以保证用不同的对象调用成员函数是对不同对象的操作。
7.对象赋值语句
对于同一个类生成的两个对象,可以进行赋值,其功能是将一个对象的数据成员赋值到另一个对象中去,赋值语句的左右两边各是一个对象名。
如,已知一个类Example,则:
Exampleobj1,obj2;
obj2=obj1;//将对象obj1的数据成员赋给对象obj2
8.对象的作用域和生存期
在不同的位置以不同的方式定义对象时,其作用域和生存期是不同的,其原理与普通变量相同。
可分为局部对象(不包括局部静态对象)、静态对象(局部静态对象和全局静态对象)、全局对象等。
9.堆对象
所谓堆对象是指在程序运行过程中,根据需要随时可以建立和删除的对象。
堆对象被创建在内存中一些空闲的存储单元中,这些存储单元被称为堆。
堆可以被创建的对象占有,可以通过删除堆对象而获得释放。
需要利用new运算符创建堆对象,利用delete运算符删除堆对象。
堆对象的生存期是整个程序的生命期。
如:
Example*p;//p为指向类Example对象的一个指针
p=newExample();//使用new给p分配内存空间
deletep;//使用delete释放p所指向的空间
10.对象数组
当一个数组的类型为类类型时,该数组中的每个元素都是该类中的一个对象,则这种数组就是对象数组。
对象数组的定义格式为:
类名数组名[数组大小];
如:
Examplearray[10];
表明array数组是一个一维对象数组,该数组有10个元素,从array[0]到array[9],其中每个元素都是类Example的对象。
第二章构造函数和析构函数
构造函数和析构函数是类的两个特殊的成员函数
1.构造函数
构造函数(constructor)是类的一个特殊的成员函数,它与类名同名。
当定义该类的对象时,构造函数将被系统自动调用用以实现对该对象的初始化。
构造函数不能有返回值,因而不能指定包括void在内的任何返回值类型。
构造函数的定义与其他成员函数的定义一样可以放在类内或类外。
构造函数的定义格式为:
类名(形参说明)
{函数体}
构造函数既可以定义成有参函数,也可以定义成无参函数,要根据问题的需要来定。
注意:
程序中不能直接调用构造函数,构造函数是在创建对象时由系统直接调用的,因此,在构造函数中一般完成初始化类成员变量的操作。
2.构造函数的重载
一个类中出现了两个以上的同名的成员函数时,称为类的成员函数的重载。
在类的成员函数的重载中,比较常见形式是构造函数的重载,当类中出现了重载构造函数时,C++语言将根据构造函数中的参数个数和类型选择合适的构造函数来完成对象的构造。
3.默认构造函数与缺省参数的构造函数
如果在类中没有显示定义构造函数,则编译系统会为该类提供一个默认的构造函数,该默认构造函数是一个无参函数,函数体为空,它仅仅负责创建对象,而不做任何初始化工作(即不给相应的数据成员赋初值),所以在该类的对象创建时不能保证有一个确定的初始状态。
良好的编程习惯应该是给类提供合适的完成初始化工作的构造函数。
但是,只要一个类定义了一个构造函数(不一定是无参构造函数),编译系统就不再提供默认的构造函数。
当构造函数具有缺省参数时,称为具有缺省参数的构造函数,在使用具有缺省参数的构造函数时,要防止二义性。
4.拷贝构造函数
拷贝构造函数是一种特殊的构造函数。
定义拷贝构造函数的一般格式为:
类名:
:
类名(const类名&形式参数)
{函数体}
拷贝构造函数的函数名与类名同名。
该函数也没有返回值。
拷贝构造函数的功能是通过将一个同类对象的值拷贝给一个新对象,来完成对新对象的初始化,即用一个对象去构造另外一个对象。
如果在类的定义中没有定义拷贝构造函数,则编译系统将自动生成一个具有上述形式的默认的拷贝构造函数,作为该类的公有成员。
5.析构函数
与构造函数对应的是析构函数。
当一个对象被定义时,系统会自动调用构造函数为该对象分配相应的资源,当对象使用完毕后且在对象消失前,系统会自动调用类的析构函数来释放这些系统资源。
析构函数也是类的一个特殊的成员函数,其函数名称是在类名的前面加上“~”;它没有返回值,也没有参数。
一个类中只能拥有一个析构函数,所以析构函数不能重载。
析构函数的定义方式为:
~类名()
{函数体}
如果程序员在定义类时没有为类提供析构函数,则系统会自动创建一个默认的析构函数,其形式为:
~类名()
{}
对象被析构的顺序与其创建时的顺序正好相反,即最后构造的对象最先被析构。
如果一个对象是被new运算符动态创建的,当使用delete运算符释放它时,delete将会自动调用析构函数。
6.一个类的对象作为另一个类的数据成员
当一个类中的数据成员是某一个类的对象时,可称这种成员是新建类的子对象或对象成员,则新类的定义格式可表示为:
calssX
{
类名1成员名1;
类名2成员名2;
类名3成员名3;
……………
类名n成员名n;
….…………//其他成员
};
其中,X为新建类的类名,类名1、类名2、……、类名n必须是已定义过的类。
如:
classA{};
classB
{
Aa;
};
则在创建类B的对象(调用类B的构造函数)时,会自动调用类A的构造函数。
如果类A的构造函数为有参函数时,通常采用初始化表的方式来调用构造函数。
新类的构造函数的一般定义格式为:
新类(参数表0):
成员1(参数表1),成员2(参数表2),…,成员n(参数表n)
{……..}
其中,成员1、成员2、……、成员n是新类中的对象成员;参数表1提供初始化成员1所需的参数,参数表2提供初始化成员2所需的参数,依此类推,并且这几个参数表中的参数均来自参数表0.另外,初始化新类的非对象成员所需的参数,也由参数表0提供。
7.常对象与常对象成员
(1)常对象
常对象是指对象常量,其定义格式为:
const类名对象名;
从格式中可以看出,常对象的定义与一般对象的定义相比,在类名前必须加const关键字。
常对象具有以下特点:
l常对象在定义时必须进行初始化,而且在程序中不能再对其进行更新。
l通过常对象只能调用类中的常成员函数,而不能调用类中的其他成员函数。
(2)常对象成员
常对象成员分为常成员函数和常数据成员。
1)常成员函数
在类中,使用关键字const说明的成员函数成为常成员函数,常成员函数的说明格式为:
类型函数名(形参表)const;
类中的常成员函数与普通成员函数相比,具有以下特点:
l常成员函数为类的只读函数,这种成员函数可以读取数据成员的值,但不可以更新数据成员的值,它也不能调用该类中没有const修饰的其他成员函数。
l常成员函数定义中的const关键字是函数类型的一部分,因此在其实现部分中也要带上const关键字。
l常成员函数定义中的const关键字可以参与区分重载函数。
例如:
#include
classTest_const{
private:
intm;
public:
Test_const(intarg1)//构造函数
{
m=arg1;
}
voidsetvalue(intnewvalue);
voidshowvalue();
voidshowvalue()const;//常成员函数
};
voidTest_const:
:
setvalue(intnewvalue)
{
m=newvalue;
}
voidTest_const:
:
showvalue()
{
cout<<"m="< } voidTest_const: : showvalue()const//此处的const关键字不可少 { cout<<"constexamplem="< } voidmain() { Test_constc1(100);//定义对象c1 constTest_constc2(100);//定义常对象c2 c1.setvalue(200); c1.showvalue();//此处调用的是函数voidshowvalue(); c2.showvalue();//此处调用的是函数voidshowvalue()const; //不能执行语句c2.setvalue(200);因为常对象c2只能调用常成员函数 } 2)常数据成员 类中定义的数据成员,除了可以为一般变量外,还可以为const常量,这种数据成员称为常数据成员。 构造函数可以对对象的数据成员进行初始化,但如果数据成员为常量成员或引用成员时,则不能在构造函数中直接用赋值语句为其进行赋值。 需要利用构造函数所附带的初始化表进行初始化,即在构造函数的括号后面加上“: ”和初始化表,其格式为: 类名: : 类名(形参表): 常数据成员名1(值1),常数据成员名2(值2),…… { //构造函数的函数体 } 可以看出,当有多个数据成员时,初始化表中的初始化项有多个,且需要用逗号隔开。 8.类作用域 类作用域又可称为类域,它是指在类定义中用一对大括号开括起来的范围。 不同的类的成员函数可以具有相同的名字,因此,需要用作用域运算符“: : ”来指明该成员函数所属的类。 在类的成员函数中可以直接引用类的数据成员。 但是,如果在成员函数中定义了同名的局部变量时,则必须用作用域运算符“: : ”来指定,以免混乱。 例如: #include classRegion{ private: intx; inty; public: Region(intx,inty) { Region: : x=x; Region: : y=y; } voidprint() { cout<<"x="< } }; voidmain() { Regionregion(5,10); Region*p; p=®ion; region.print(); p->print(); } 第三章继承和派生 继承性是面向对象程序设计最重要的机制之一。 1.继承的概念 所谓继承(inheritance)就是利用已有的数据类型定义出新的数据类型。 在继承关系中,被继承的类称为基类(baseclass)或父类,而通过继承关系定义出来的新类则被称为派生类(derivedclass)或子类。 派生类既可以对基类的性质进行扩展又可以对基类进行限制,从而得到更加灵活、适用的可重用模块,大大缩短了程序的开发时间。 一个派生类既可以从一个基类派生也可以从多个基类派生。 从一个基类派生称为单继承;从多个基类派生称为多重继承。 (1)单继承 单继承的定义形式如下: class派生类名: 访问方式基类名 { 派生类中的新成员 }; 其中,派生类名是新定义的类名。 基类名必须是程序中已有的一个类。 在单继承中,每个类可以有多个派生类,但是每个派生类只能有一个基类。 (2)多重继承 所谓多重继承是指派生类从多个基类中派生而来的。 定义多重继承类的方式如下: class派生类名: 访问方式基类名1,访问方式基类名2…… { 派生类中的新成员 }; 从定义格式上来看,多重继承与单继承的区别主要是多重继承的基类多于一个。 (3)访问方式 不管在单继承还是在多重继承的定义格式中,访问方式,即继承方式,可以为public、private或protected,如果省略,则默认为private方式。 访问方式为public方式时,这种继承称为公有继承; 访问方式为private方式时,这种继承称为私有继承; 访问方式为protected方式时,这种继承称为保护继承。 2.派生类的生成过程 在给出了派生类的定义和相应成员函数的实现代码后,整个派生类的定义就算完成了,这是就可以利用该类定义相应的对象处理实际问题了。 由于派生类是在基类的基础上经过继承而产生的,所以搞清派生类中到底有哪些成员对于更好的使用派生类是很重要的。 事实上,派生新类经历了三个步骤: (1)吸收基类成员 派生类继承吸收了基类的全部数据成员以及除了构造函数、析构函数之外的全部函数成员。 也就是说,基类中的构造函数和析构函数使不能继承到派生类中的。 (2)改造基类成员 对继承到派生类中基类成员的改造包括两个方面: 一是基类成员的访问方式问题,这由派生类定义时的访问方式来控制;二是对基类数据成员或成员函数的覆盖,也就是在派生类中定义了与基类中同名的数据成员或函数成员,由于作用域不同,于是发生同名覆盖,基类中的成员就被替换成派生类中的同名成员。 (3)添加新成员 在派生类中,除了从基类中继承过来的成员外,还可以根据需要在派生类中添加新的数据成员和成员函数,以此实现必要的新功能。 可以看出,在派生类中可以添加新成员的机制是继承和派生机制的核心,保证了派生类在功能上比基类有所发展。 3.继承方式对基类成员的访问控制 前面已经分析,派生类继承和吸收了基类的全部数据成员和除了构造函数、析构函数之外的全部函数成员,但这些成员在派生类中的访问属性是可以调整的,这是由派生类定义格式中的继承方式来决定的,也就是继承方式控制了基类中具有不同访问属性的成员在派生类中的访问属性。 由于继承方式可以有public、private和protected三种,不同的继承方式会导致原来具有不同访问属性的基类成员在派生类中的访问属性也有所不同。 这种访问包括两个方面: 一是派生类中新增成员对从基类继承来的成员的访问;二是派生类的外部通过派生类的对象从基类继承来的成员的访问。 (1)公有继承 当类的继承方式为公有继承时,基类中public和protected成员的访问属性在派生类中不变,而基类private成员不可访问。 也就是说,基类的public和protected成员在公有继承方式下分别继承为派生类的public和protected成员,派生类中的其他成员可以直接访问它们,在派生类的外部只能通过派生类的对象访问从基类继承来的public成员。 而无论是派生类的成员还是派生类的对象都无法访问从基类继承来的private成员。 (2)私有继承 当类的继承方式为私有继承时,基类中的public和protected成员都以private成员出现在派生类中,而基类private成员不可访问。 也就是说,基类的public和protected成员在私有继承方式下被继承为派生类的private成员,派生类中的其他成员可以直接访问它们,但在派生类的外部无法通过派生类的对象访问它们。 而无论是派生类的成员还是派生类的对象都无法访问从基类继承来的private成员。 可以看出,经过私有继承后,所有基类的成员都成为派生类的私有成员,如果进一步派生的话,基类的成员就无法在新的派生类中被访问。 因此,经过私有继承后,基类的成员再也无法在以后的派生类中发挥作用,实际是相当于中止了基类功能的继续派生。 (3)保护方式 当类的继承方式为保护继承时,基类中的public和protected成员都以protected成员出现在派生类中,而基类private成员不可访问。 也就是说,基类的public和protected成员在保护继承方式下被继承为派生类的protected成员,派生类中的其他成员可以直接访问它们,但在派生类的外部无法通过派生类的对象访问它们。 而无论是派生类的成员还是派生类的对象都无法访问从基类继承来的private成员。 4.派生类的构造函数和析构函数 继承和派生的机制可以使派生类继承基类的成员,从而实现了原有代码的重用,但是,由于基类的构造函数和析构函数不能继承,那么在派生类中,如果对派生类新增的成员进行初始化,就必须在派生类中根据需要加入新的构造函数,如果对从基类继承下来的成员进行初始化,还必须由基类的构造函数来完成,所以需要在派生类中的构造函数,一方面负责调用基类的构造函数对基类成员进行初始化,另一方面还要负责对基类的构造函数所需要的参数进行必要的设置。 (1)单继承方式下派生类构造函数的定义 在单继承方式下,派生类的构造函数的定义格式如下: 派生类名: : 派生类构造函数名(形参表): 基类构造函数名(参数表) { //派生类构造函数的函数体 } 在此定义格式中,派生类构造函数名后面括号内的参数表中包括参数的类型和参数名,而基类构造函数名后面括号内的参数表中只有参数名而没有参数类型,并且这些参数必须是来源于派生类构造函数名后面括号内的参数。 (2)多重继承方式下的派生类构造函数的定义 在多重继承方式下,派生类的构造函数必须同时负责所有基类构造函数的调用,对于派生类构造函数的参数个数必须同时满足多个基类初始化的需要。 所以,在多重继承方式下,派生类的构造函数的定义格式如下: 派生类名: : 派生类构造函数名(参数表): 基类名1(参数表1)基类名2(参数表2)…… { //派生类构造函数的函数体 } 其中,第1个参数表中的参数包含了其后的各个参数表中的参数。 (3)派生类构造函数的执行次序 派生类构造函数执行的一般次序为: 1)调用基类构造函数,调用顺序按照它们被继承时说明的顺序(从左到右),而不是按派生类构造函数在初始化表中的次序; 2)调用子对象的构造函数(如果在派生类中存在子对象的话),调用顺序按照它们在类中说明的顺序; 3)执行派生类构造函数的函数题。 当派生类的对象被删除时,派生类的析构函数被执行。 由于基类的析构函数不能被继承,因此在执行派生类的析构函数时,基类的析构函数也将被调用。 而执行顺序是先执行派生类的析构函数,再执行基类的析构函数,其顺序与执行构造函数是的顺序正好相反。 5.虚基类 在多重继承的情况下,派生类具有两个以上的直接基类,而这些直接基类的一部分或全部又是从另一个共同基类派生而来的,这些直接基类中从上一级基类继承来的成员拥有相同的名称,在派生类的对象中,这些同名成员在内存中同时拥有多个拷贝,如何进行分辨呢? 有两种方法,一是使用作用域运算符唯一标帜并分别访问它们;二是将直接基类的共同你基类设置为虚基类。 (1)使用作用域运算符方法 这种方法就是在需要访问的成员名前加上直接基类名和作用域运算符“: : “。 其格式是: 直接基类名: : 数据成员名 直接基类名: 成员函数名(参数表) (2)虚基类的方法 该方法就是将直接基类的共同基类设置为虚基类,即在基类的访问方式前加上关键字“virtual“,声明虚基类的格式如下: class派生类名: virtual访问方式基类名 {//声明派生类成员}; 虚基类虽然被一个派生类间接地多次继承,但派生类却只继承一份该基类的成员,这样就避免了在派生类中访问这些成员时的二义性。 6.虚基类机制下的构造函数的执行顺序 虚基类机制下的构造函数的执行顺序与一般多重继承下的构造函数的执行顺序是不同的,其执行顺序如下: (1)一个类的所有直接基类中,虚基类的构造函数在非虚基类之前调用; (2)如果一个类的所有直接基类中有多个虚基类,则这些虚基类的构造函数的执行顺序与在派生类中的说明的次序相同; (3)若虚基类由非虚基类派生而来,则仍然先调用基类构造函数,再按照派生类中构造函数的执行顺序调用。 第四章虚函数和多态 多态性与前面提到的数据封装和继承性共同构成了面向对象程序设计的三个重要机制。 1.静态联编与动态联编 由于函数重载的存在,当程序中出现
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- C+ 复习资料