在上一章节中,我们讲解了封装,类,this指针,这一章节我们来讲解继承,多层继承,多重继承,构造函数和析构函数。
一.构造函数
构造函数时类的特殊成员函数,通常用于初始化对象。构造函数在创建对象时由编译器自动调用。C++中的每个类至少要有一个构造函数,如果类中没有定义构造函数,编译器会默认提供一个无参构造函数,默认的无参构造函数体也为空,不具有初始化意义。因此,在C++程序中要显示定义构造函数。
构造函数的定义
构造函数是类的特殊成员函数,C++编译器严格规定了构造函数的接口形式,其定义格式如下图所示:
class 类名{
权限控制符:
构造函数名(参数列表){
函数体
}
...
}
关于构造函数定义格式的说明,具体如下:
1.构造函数必须与类名相同
2.构造函数名的前面不需要设置返回值类型
3.构造函数中无返回值,不能使用return返回
4.构造函数的成员权限一般设置为public
接下来我们根据具体的例子来为大家讲解:
<1>.无参构造函数
#include <stdio.h>
struct Student{
int a;
int b;
Student(){
}
Student(int c,int d){
a = c;
b = d;
}
};
void Test(){
Student s1;
}
int main(){
Test;
return 0;
}
我们先来看这个无参构造函数,其作用相当于创建一个类的对象,没有赋初值。相当于C语言中的struct 函数题名 函数体变量
。
<2>.有参构造函数
接下来我们来看看有参数的构造函数:
#include <stdio.h>
struct Student{
int a;
int b;
Student(int c,int d){
a = c;
b = d;
}
};
void Test(){
Student s1(1,2);
printf("%d %d",s1.a,s1.b);
}
int main(){
Test();
return 0;
}
我们可以看到程序输出窗口输出了1和2,分别为s1中a的值和b的值。
那么我们就知道,带参数的构造函数完成了创建类的对象并且初始化对象。其作用相当于C语言中Student s1;s1.a=1;s1.b=2;
<3>.重载构造函数
我们在使用类的时候,有时候需要初始化对象,有时候不需要初始化对象,那么构造函数如何完成呢?我们可以用到函数重载:
struct Student{
int a;
int b;
Student(){
}
Student(int c,int d){
a = c;
b = d;
}
};
我们可以像这样定义类,当我们不需要初始化对象的时候,我们可以这样写:
Student s1;
当我们需要初始化对象的时候,我们就可以传入参数:
Student s1(1,2);
这样我们就可以将对象初始化了,使用起来非常方便。
二.析构函数
创建对象时,系统会为对象分配所需要的内存空间等资源,当程序结束或对象被释放时,系统为对象分配的资源也需要回收,以便可以重新分配给其他对象使用。在C++中,对象资源的释放通过析构函数来完成。析构函数的作用是在对象被释放之前完成一些清理工作。
析构函数的定义
与构造函数一样,析构函数也是类的特殊成员函数,其定义格式如下:
class 类名
{
~析构函数名称(){
}
...
}
关于析构函数的定义,有以下注意事项:
1.析构函数必须与类名相同,在析构函数名称前面添加“~”符号
2.析构函数只能有一个,不能重载
3.不能带任何参数
4.不能带任何返回值
案例
通常情况下,当我们在创建类时,如果有动态申请内存的话,析构函数就显得特别重要了。
析构函数的使用案例如下:
#include <stdio.h>
struct Student{
int a;
int b;
Student(){
}
Student(int c,int d){
a = c;
b = d;
}
~Student(){
}
};
void Test(){
Student s1;
}
int main(){
Test();
return 0;
}
三.继承,多层继承,多重继承
在创建类的时候,我们通常会有这样的需求,就是好几个类中都有相同的成员,那么C++帮我们很好的解决了代码重复的问题,所谓继承,就是从“先辈”处获得特性。
在C++中,继承就是在原有类的基础上产生新类,新类会继承原类所有的属性和方法。
基类:
原有的类成为基类或父类
派生类,子类:
新类成为派生类或子类。
派生类同样可以作为基类派生出新类。在多层继承结构中,派生类上一层的基类成为直接基类,各层次的基类成为间接基类。
在C++中,声明一个类继承另一个类的格式如下:
class 派生类名称:继承方式 基类名称{
派生类成员声明;
}
我们来通过一个例子说明:
struct Base{
int name;
int age;
};
struct Teacher{
int name;
int age;
int id;
}
struct Student3{
int name;
int age;
int id;
}
我们可以看到在Teacher类中和Student类中都由name属性和age属性,这时候我们就可以通过继承来减少重复代码:
struct Base{
int name;
int age;
};
struct Teacher:Base{
int id;
};
struct Student3:Base{
int id;
};
我们可以通过继承来实现,效果和前面一样,我们可以通过反汇编来查看:
#include <stdio.h>
struct Base{
int name;
int age;
};
struct Teacher0:Base{
int id;
};
struct Teacher{
int name;
int age;
int id;
}
void Test(){
Teacher t;
t.age = 1;
t.name = 2;
t.id = 3;
}
int main(){
Test();
return 0;
}
我们先通过将成员全部定义在一个类中的方式,来到反汇编查看:
mov dword ptr [ebp-8],1
mov dword ptr [ebp-0Ch],2
mov dword ptr [ebp-4],3
我们再通过函数继承的方式看一下:
#include <stdio.h>
struct Base{
int name;
int age;
};
struct Teacher:Base{
int id;
};
void Test(){
Teacher t;
t.age = 1;
t.name = 2;
t.id = 3;
}
int main(){
Test();
return 0;
}
我们继续来到反汇编查看:
mov dword ptr [ebp-8],1
mov dword ptr [ebp-0Ch],2
mov dword ptr [ebp-4],3
我们发现在计算机底层,他俩其实其实都一样。但是在我们使用C++的时候,可以大大减少我们的代码。
上述这种方式我们称为继承,我们来讲讲多重继承,我们直接给出例子,相信大家可以理解:
struct Base1{
int a;
int b;
};
struct Base2{
int c;
int d;
};
struct Teacher:Base1,Base2{
int e;
int f;
};
像这种同时继承了两个父元素的,我们称为多重继承。
需要注意的是: 当我们使用继承时,父元素会内的属性会在派生类的前面,比如上面这个多重继承:
struct Teacher{
int a;
int b;
int c;
int d;
int e;
int f;
};
多层继承:
struct Base1{
int a;
int b;
};
struct Base2:Base1{
int c;
int d;
};
struct Teacher:Base2{
int e;
int f;
};
像这样,父类又有父类(我们可以形象地成为‘爷爷类’),就称为多层继承。
在最后必须要提醒一点:可以使用父类的指针指向子类对象,但最好不要用子类的指针指向父类对象(因为子类对象的属性多于父类,子类指针指向父类元素的话,可能会让指针指向本不属于父类的属性),如果这样做的话,会有安全问题