类和对象初步
面向过程的程序设计——结构化程序设计
特点
程序=数据结构+算法
数据结构对应变量,算法对应函数;
算法用来操作数据结构;
一个大问题被分解成许多小问题的组合,也就是整个程序被分解成一个个的函数;“过程”,即由函数来实现。
缺点
- 难以理解和维护
函数和它操作的变量是分离的;
(在大型程序中,搞清楚哪些函数会访问哪些全局变量,哪些全局变量又被哪些函数访问是一件很麻烦的事情)
函数之间的调用关系也是错综复杂的; - 难以修改和扩充
结构化程序设计中可以任意访问变量,那么当该变量的定义有改动时,所有相关的语句都要找出来修改,十分麻烦; - 难以重用
很难直接抽取出可重用的代码;
(因为它可能会调用一些新程序用不到的其他函数,也可能会访问某些全局变量;总之就是程序的各个模块有着千丝万缕的联系,很难让某个模块的代码“独善其身”)
面向对象的程序设计
特点
程序=类+类+类+……
- 抽象——概括同一类事物的共同特点;
“对象”——各种事物;
对象的特点=属性+方法
属性——变量
方法——函数
方法即为对象的行为,以及能对对象进行的操作;
- 封装——通过某种语法形式将数据和相应的算法捆绑,写成一个整体,“类”
类=属性(数据结构)+方法(操作数据结构的算法)
- 继承——新类可以从现有的类派生而来
- 多态——不同类的对象有名称相同的行为,而具体行为的实现方式不同
类的定义和使用
类的成员=成员变量+成员函数
类的定义
类的成员函数和定义可以分开写
可以仅在类的定义中进行成员函数的声明;
在类的外部具体写出成员函数的定义;
在定义好“类”之后定义“对象”
“对象”——通过“类”定义出来的变量
对象的内存分配
- 每个对象都有自己的存储空间
- 成员变量是每个对象各自存有一份;一个对象的成员变量改变,不会影响另一个对象
- 成员函数和普通函数一样,并非每个对象各自存有一份,而是在整个内存中只有一份;但它可以作用于不同的对象
- 一个对象占用的内存空间=其成员变量的体积之和
- 用sizeof()计算
对象间的运算
可以用“=”进行对象间的赋值;
(这个过程中实际上调用了复制构造函数,以后再详细做笔记)
但不能用各种比较运算符进行比较;(除非经过重载)
如何使用类的成员变量和成员函数
- 对象名.成员名;
- 指针->成员名;
- 引用名.成员名;
“成员函数作用在某个对象a上”——进入该成员函数时,函数中访问到的成员变量是属于a的;
调用类的成员函数时,必须指明它作用的对象;(否则,如果定义了多个同一类的对象,它们的成员变量名相同,但是因为每个对象各自有自己的内存空间,于是编译器不知道这个类的成员函数该访问哪个成员变量)
class和struct的区别
- 有成员函数的struct就是class;
- 没有成员函数的struct还是称为“结构”,这种结构变量不能算作对象;
- 写成struct的类和写成class的类只在类成员的可访问范围上有差距;
类成员的可访问范围
访问范围说明符号
- private
- public
- protected
如果没有访问范围说明符,class默认为私有,struct默认为公有。
私有成员和公有成员的区别
私有成员——只能在这个类的成员函数的内部被访问;
公有成员——在任何地方都能被访问;
换句话说,在类的成员函数的函数体内部,可以访问当前对象以及同类的其他对象的全部成员变量及函数;在外部,只能访问该类对象的公有成员。
实例
//定义一个名为“CEmployee”的类
class CEmployee {
private:
char szName[30];
public:
int salary;
void setName(const char *name);
void getName(const char* name);
void averageSalary(CEmployee e1, CEmployee e2);
};
szName是一个私有的成员变量;
salary是一个公有的成员变量;
在外部定义其成员函数如下:
void CEmployee::setName(const char *name)
{
strcpy(szName, name);
//OK!在成员函数内部,可以访问私有成员szName;
}
void CEmployee::getName(const char *name)
{
strcpy(szName, name);
//OK!在成员函数内部,可以访问私有成员szName;
}
void CEmployee::averageSalary(CEmployee e1, CEmployee e2)
{
salary = (e1.salary + e2.salary) / 2;
//OK!在成员函数内部,可以访问同类的其他对象的成员;
}
下面看主函数,它在成员函数的外部。
int main()
{
CEmployee e;//定义对象e
strcpy(e.szName, "Tom");
//WRONG!在成员函数外部,不能访问其私有成员szName!
e.setName("Tom");
//OK!在成员函数外部,可以访问其公有成员函数
e.salary = 5000;
//OK!在成员函数外部,可以访问其公有成员变量
return 0;
}
隐藏——设置私有成员的机制
- 利于程序修改
强制对私有成员变量的访问通过成员函数进行;好处是:如果以后成员变量的类型的属性修改了,只需要修改成员函数,而不用找出所有的函数中直接访问成员变量的语句进行修改; - 避免对成员对象的不正确操作
内联成员函数
两种实现
- 在声明成员函数的时候加上Inline关键字;
- 直接将成员函数的函数体写在类的定义里面;
相当于用空间换时间。
当该函数本身语句较少,在程序中要反复调用,并且调用它的时间开销如果不能小于它的执行开销,那么不如写成内联函数;但如果一个函数本身执行时间就很长,调用它的开销只是一小部分,那么写成内联函数就只会带来额外的内存开销。
注意事项
类的定义
成员变量不能为自身类型,但可以是自身类型的指针或引用。
原因见链接:
为什么C++类定义中,数据成员不能被指定为自身类型,但可以是指向自身类型的指针或引用?.
从内存分配的角度来说,当类的定义还没有完成的时候,编译器是无法计算给当前类的对象分配多少空间的,于是会出错;但是指针或者引用(引用的本质是指针常量),它们的空间是固定的,与类型无关。
从对象构造的角度来说,会出现无限初始化的现象。(详见构造函数的相关讲解)