面向过程编程:
关注是问题解决的过程步骤(事情是如何解决的),算法。
面向对象编程:
关注的是谁能解决问题(类),需要什么样的数据(成员变量),具备什么样的技能(成员函数)才能解决问题。
抽象:
找出一个能够解决问题的"对象"(观察研究对象),找出解决所必须的数据(属性)、功能(成员函数)。
封装:
把抽象的结构,归结为一个类(数据类型),然后实例化出类对象,设置对象的属性,调用对象的功能达到解决问题的目的。
继承:
在解决问题先,先寻找之前的类能不解决问题,或解决部分问题,如果可以则把旧的类型继承后再次拓展,来缩短解决问题的时间,降低解决问题的难度。
多态:
对象的多种形态,外部看到个对象,然后向对象发出指令,对象会根据自身情况情况做出独特反应。
一、类和对象
1、通过分析"对象"的属性和行为设计出一个类。
2、类就是数据类型
简单类型:只能表示一个属性(变量),C/C++内建数据类型
数组类型:可以表示多个属性(变量),类型必须相同。
结构类型:可以表示多个属性(变量),但缺少行为(函数)。
类类型:既能表示属性,也能表示行为,一种复合数据类型。
3、对象就类这种数据类型创建出的实例,相当于结构变量。
class Student
{
成员变量;
成员函数;
};
Student stu;
二、类的定义与实例化
1、类的一般形式
class 类名 : 继承方式 父类
{
public/private/protected: //访问控制限制符
成员变量;
// 构造函数
类名(形参表)
{
}
// 析构函数
~类名(void)
{
}
};
2、类的访问控制限定符
public:公有成员,在任何位置都可以访问
private:私有成员,只能类(自己)的成员函数中访问
protected:受保护成员,只能在类(自己)和子类中访问
注意: 类中的成员变量、成员函数默认是 private,结构中的成员变量、成员函数默认是 public。
C++中类和结构的区别只有成员函数和成员变量默认访问权限不同。
3、构造函数
(1)什么是构造函数:类的同名函数就是构造函数,没有返回值。
(2)什么时候调用,谁调用,调用几次?
创建类型对象时会被自己调用(每创建一个类对象就会调用一次),对象整个生命周期中一定会被调用一次,只能被调用一次。
(3)负责干什么
成员变量的初始化,分配相关资源,设置对象的初始状态。
class 类名 : 继承方式 父类
{
// 构造函数
类名(形参表)
{
}
};
(4)对象的创建过程
A、分配类型所需要空间,无论栈还是堆。
B、传递实参调用构造函数,完成如下任务:
a、根据继承表依次调用父类的构造函数。
b、根据成员变量的顺序依次调用成员变量的构建函数。
c、执行构造函数体中的代码。
注意:执行构建函数的代码是整个构造函数的最后一步,
要保证构造函数代码所需要的一切资源和先决条件在该代码执行前已经准备充分,并得到正确的初始化。
(5)对象的创建方法
在栈上创建:类名 对象; // 不需要括号
类名 对象(实参);
在堆上创建:类名* 对象指针 = new 类名;
类名* 对象指针 = new 类名(实参);
创建多个对象:
类名 对象 = {(实参),(实参),(实参)};
类名* 对象指针 = new 类名[n]{(实参),(实参),(实参)};
注意: 通过malloc创建的类对象不能调用构造函数。
通过new[]创建的对象,一定要通过delete[]释放。
(6)类的声明、实现、调用
A、在头文件中声明
class 类名 : 继承方式 父类
{
成员变量;
public: //访问控制限制符
// 构造函数
类名(形参表);
// 析构函数
~类名(void);
// 其它成员函数
返回值 函数名(参数列表);
};
B、源文件实现类的相关函数
返回值 类名::函数名(参数列表)
{
}
C、调用时只需要导入头文件,然后与类函数所在的源文件一起编译即可。
注意: 如果一个类内容不多,可以考虑在头文件完全实现。也可以只在头文件实现一些简单和成员函数。
类中自动生成的函数,在源文件中实现时,也需要在头文件中声明。
三、构造函数与初始化列表
1、构造函数可以被重载(同一个名字的函数有多个不同版本)
2、缺省构造是编译器自动生成的一个什么都不做的构造函数(唯一的作用就避免统错误)。
注意:当类实现一个缺省构造后,缺省构造就不会再自动生成,如果有需要必须显式地写出来。
3、无参构造未必无参,当给有参构造的所有参数设置默认形参,调用这种构造函数就不需要传参。
注意:所谓的"编译器生成的某某函数"其实不是真正语法意义上的函数,而是功能意义的函数,
编译器作为可执行指令的生成者,它会直接生成具有某项功能的二进制指令,不需要借助高级
语言语义上的函数完成此任务。
注意:如果一个类A是其它类B成员变量,那么一定要保证它有一个无参构造,当B的构造函数
执行时会先执行成员变量的无参构造,而此时类B是无法给类A成员变量提供参数的。
4、单参构造与类型转换
如果构造函数的参数只有一个,那么Test t = n语句就不会出错,它会自动调用单参构造来达到
类型转换的效果。
如果想禁止这种类型转换需要在单参构造前加 explicit
5、初始化列表
为类型员进行初始化用的。
构造函数(参数):成员1(参数1),成员2(参数2)....
通过初始化列表可以类成员变量传递参数,以此调用类成员的有参构造。
初始化列表也可以给 const 成员、引用成员进行初始化。
成员的初始化顺序与初始化列表没有关系,而是在类中的定义顺序有关。
注意:初始化列表运行类成员变量还没有定义成功。
四、析构函数
1. 特殊的成员函数
函数名必须是
~类名(void)
{
}
没有参数 没有返回值、不能重载
2. 谁来调用
析构函数会在销毁对象时自动调用,在对象的整个生命周期内最多被调用一次。
3. 析构函数负责什么
析构函数负责释放在构造函数期间所获取到的所有资源,它的执行过程:
1. 先执行析构函数本身代码
2. 调用成员类的析构函数
3. 调用父类的析构函数
4. 缺省的析构函数
如果一个类没有实现析构函数,编译器会自动生成一个具有析构函数功能的二进制指令,
它负责释放编译器能够看得到的资源(成员变量、类成员、父类成员),这就是缺省析构。
如果一个类没有动态资源,也不需要做善后工作,缺省析构就完全够用了,不需要在实现新的析构函数。
注意:缺省析构无法释放动态资源(堆内存)