本人微信公众号"aeolian"~

C++类与对象(05)

类是具有惟一标识符的实体;在类中声明的任何成员不能使用extern、auto和register关键字进行修饰;类中声明的变量属于该类,在某些情况下,变量也可以被该类的不同实例所共享。

访问权限用于控制对象的某个成员在程序中的可访问性,如果没有使用关键字,则所有成员默认声明为private权限。

定义成员函数

类中声明的成员函数用来对数据成员进行操作,还必须在程序中实现这些成员函数。 定义成员函数的一般形式如下:  

返回类型 类名::成员函数名(参数列表) {  成员函数的函数体//内部实现 }  

其中“::”是作用域运算符,“类名”是成员函数所属类的名字,“::”用于表名其后的成员函数是属于这个特定的类。换言之,“类名::成员函数名”的意思就是对属于“类名”的成员函数进行定义,而“返回类型”则是这个成员函数返回值的类型。  也可以使用关键字inline将成员函数定义为内联函数。  如果在声明类的同时,在类体内给出成员函数的定义,则默认为内联函数。

数据成员的赋值

不能在类体内给数据成员赋值。在类体外就更不允许了。 数据成员的具体值是用来描述对象的属性的。只有产生了一个具体的对象,这些数据值才有意义。如果在产生对象时就使对象的数据成员具有指定值,则称为对象的初始化

使用类的对象

 对象和引用都使用运算符“.”访问对象的成员,指针则使用“- >”运算符。

(1)类的成员函数可以直接使用自己类的私有成员(数据成员和成员函数)

(2)类外面的函数不能直接访问类的私有成员(数据成员和成员函数)

(3)类外面的函数只能通过类的对象使用该类的公有成员函数。

在程序运行时,通过为对象分配内存来创建对象。在创建对象时,使用类作为样板,故称对象为类的实例。  

定义类对象指针的语法如下:

类名* 对象指针名;  

对象指针名=对象的地址;

也可以直接进行初始化。  

类名* 对象指针名=对象的地址;  

类对象的指针可以通过“->”运算符访问对象的成员,即: 对象指针名->对象成员名

数据封装

面向对象的程序设计是通过为数据和代码建立分块的内存区域,以便提供对程序进行模块化的一种程序设计方法,这些模块可以被用做样板,在需要时在建立其副本。根据这个定义,对象是计算机内存中的一块区域,通过将内存分块,每个模块(即对象)在功能上保持相对独立。  对象被视为能做出动作的实体,对象使用这些动作完成相互之间的作用。换句话说,对象就像在宿主计算机上拥有数据和代码并能相互通信的具有特定功能的一台较小的计算机。

 

运算符new

 

 运算符new用于建立生存期可控的对象,new返回这个对象的指针。由于类名被视为一个类型名,因此,使用new建立动态对象的语法和建立动态变量的语法类似,其不同点是new和构造函数一起使用。使用new建立的动态对象只能用delete删除,以便释放所占空间。应养成及时释放不再使用的内存空间的习惯。 

赋值构造函数

编译器建立一个默认复制构造函数,默认复制构造函数采用拷贝方式使用已有的对象来建立新对象,所以又直译为拷贝构造函数。程序员可以自己定义复制构造函数,对类A而言,复制构造函数的原型如下:  

A::A(A&)  

从这个函数原型来看,首先它是一个构造函数,因为这毕竟是在创造一个新对象。其次,他的参数有些特别,是引用类自己的对象,即用一个已有的对象来建立新对象。使用引用是从程序的执行效率角度考虑的。为了不改变原有对象,更普通的形式是像下面这样使用const限定:  A::A(const A &) 

析构函数

在对象消失时,应使用析构函数释放由构造函数分配的内存。构造函数、赋值构造函数和析构函数是构造型成员函数的基本成员,应深刻理解他们的作用并熟练掌握其设计方法。

调用析构函数也是由编译器来完成的,所以编译器必须总能知道应调用哪个函数。最容易、也最符合逻辑的方法是指定这个函数的名称与类名一样。为了与析构函数区分,在析构函数的前面加上一个“~”号(仍然称析构函数与类同名)。在定义析构函数时,不能指定任何返回类型,即使指定void类型返回类型也不行。析构函数也不能指定参数,但是可以显示地说明参数为void,即形如A::~A(void)。从函数重载的角度分析,一个类也只能定义一个析构函数且不能指明参数,以便编译系统自动调用。

析构函数在对象生存期结束时被自动调用。当对象的生存期结束时,程序为这个对象调用析构函数,然后回收这个对象占用的内存。全局对象和静态对象的析构函数在程序运行结束之前调用。  类的对象数组的每个元素调用一次析构函数。全局对象数组的析构函数在程序结束之前被调用。

析构函数与运算符delete

运算符delete与析构函数一起工作。当使用运算符delete删除一个动态对象时,他首先为这个动态对象调用析构函数,然后再释放这个动态对象占用的内存,这和使用new建立动态对象的过程正好相反。

当使用delete释放动态对象数组时,必须告诉这个动态对象数组有几个元素对象,C++使用“[ ]”来实现。即语句  delete[ ] ptr

当程序先后创建几个对象时,系统按后建先析构的原则析构对象。当使用delete调用析构函数时,则按delete的顺序析构。

默认析构函数

如果在定义类时没有定义析构函数,C++编译器也为它产生一个函数体为空的默认析构函数。

this指针

使用this指针,保证了每个对象可以拥有自己的数据成员,但处理这些数据成员的代码可以被所有的对象共享。

C++规定,当一个成员函数被调用系统自动向它传递一个隐含的参数,该参数是一个指向调用该函数的对象的指针,从而使成员函数知道该对哪个对象进行操作。在程序中,可以使用关键字this来引用该指针。this指针是C++实现封装的一种机制,它将对象和该对象调用的成员函数连接在一起,在外部看来,每一个对象都拥有自己的成员函数。  

除非有特殊需要,一般情况下都省略符号“this ->”,而让系统进行默认设置。

对象的性质

(1)同一个类的对象之间可以相互赋值。

(2)可使用对象数组。  

(3)可使用指向对象的指针使用取地址运算符&将一个对象的地址置于该指针中。 注意,指向对象的指针不能取数据成员的地址,也不能去成员函数的地址。  

(4)对象可以用作函数参数。对象作为函数参数时,可以使用对象、对象引用和对象指针。

(5)一个对象可以做为另一个类的成员。

类的性质

使用类的权限

数据成员为私有,成员函数为公有的情况:

(1)类本身的成员函数可以使用类的所有成员(私有和公有成员)。

(2)类的对象只能访问公有成员函数。

(3)其他函数不能使用类的私有成员,也不能使用公有成员函数,它们只能通过类的对象使用类的公有成员函数。

(4)虽然一个可以包含另外一个类的对象,但这个类也只能通过被包含类的对象使用那个类的成员函数,通过成员函数使用数据成员。

面向对象编程的文件规范

 一般要求将类的声明放在头文件中,非常简单的成员函数可以在声明中定义(默认内联函数形式),实现放在.cpp文件中。在.cpp文件中,将头文件包含进去。主程序单独使用一个文件,这就是多文件编程规范。

C++的源程序可包含各种编译指令,以指示编译器对源代码进行编译之前先对其进行预处理。所有的编译指令都以#开始,每条编译指令单独占用一行,同一行不能有其他编译指令和C++语句(注释例外)。编译指令不是C++的一部分,但扩展了C++编程环境的使用范围,从而改善程序的组织和管理。

嵌入指令:

#include指示编译器将一个源文件嵌入到带有#include指令的源文件中该指令所在的位置处。尖括号或双引号中的文件名可包含路径信息。例如:  #include<\user\prog.h> 注意:由于编译指令不是C++的一部分,因此,在这里表示反斜杠时只使用一个反斜杠。

宏定义:

#define指令定义一个标识符字符串,在源程序中每次遇到该标识符时,编译器均用定义的字符串代替之。该标识符称为宏名,而将替换过程称之为宏替换。#define指令用以进行宏定义,其一般形式如下: #define 宏名 替换正文 ,“宏名”必须是一个有效的C++标识符,“替换正文”可为任意字符组成的字符序列。“宏名”和“替换正文”之间至少有一个空格。注意,宏定义由新行结束,而不以分号结束。如果给出了分号,则它也被视作为替换正文的一部分。当替换正文要书写在多行上时,除最后一行外,每行的行尾要加上一个反斜线,表示宏定义继续到下一行。 因宏定义有许多不安全因素,对需要使用无参数宏的场合,应该尽量使用const代替宏定义。 在程序的一个地方定义的宏名,如果不想使其影响到程序的其他地方,可以在不再使用时用#undef删除

条件编译指令:

条件编译指令是#if、#else、#elif和#endif,它们构成类似于C++的if选择结构,其中#endif表示一条指令结束。  编译指令#if用于控制编译器对源程序的某部分有选择地进行编译。该部分从#if开始,到#endif结束。如果#if后的常量表达式为真,则编译这部分,否则就不编译该部分,这时,这部分代码相当于被从源文件中删除。  编译指令#else在#if测试失效的情况下建立另外一种选择。可以在#else分支中使用编译指令#error输出出错信息。#error使用的形式如下:  #error 出错信息  “出错信息”是一个字符序列。当遇到#error指令时,编译器显示其后面的“出错信息”,并中止对程序的编译。  编译指令可嵌套,嵌套规则和编译器对其的处理方式与C++的if预计嵌套情况类似。

define操作符

关键字defined不是指令,而是一个预处理操作符,用于判断一个标识符是否已经被#define定义。如果标识符identifier已被#define定义,则defined(identifier)为真,否则为假。  条件编译指令#ifdef#ifndef用于测试其后的标识符是否被#define定义,如果已经被定义,则#ifdef测试为真,#ifndef测试为假;如果没有被定义,则#ifdef测试为假,#ifndef测试为真。

在头文件中使用条件编译

友元函数

有时两个概念上相近的类要求其中一个类可以无限制地存取另一个类的成员。  友元函数解决了这类难题。友元函数可以存取私有成员、公有成员和保护成员。其实,友元函数可以是一个类或函数,尚未定义的类也可以作为友元引用。

友元函数可以在类中声明时定义。如果在类外定义,不能再使用friend关键字。

点赞

Leave a Reply

Your email address will not be published. Required fields are marked *