构造函数、析构函数、继承
1.构造函数
通过结构体创建一个对象时,需要先声明结构体变量、再给结构体每个成员赋值(也就是所谓的初始化);
如果能够在声明变量时就直接给结构成员赋值会更加方便;构造函数就是为了达到这一目的;
构造函数:
结构体中的Person函数就是构造函数
struct Person { int age; int level; Person() { printf("Person对象创建了\n"); } //构造函数 Person(int age,int level) { this->age = age; this->level = level; } //成员函数 void Print() { printf("%d-%d\n",age,level); } };
构造函数的特点:
1】与类同名
2】没有返回值
3】创建对象的时候执行
4】主要用于初始化
5】可以有多个(最好有一个无参的),称为重载 其他函数也可以重载
6】编译器不要求必须提供
重载:
当没有构造函数时,编译器默认提供一个无参数的构造函数;
声明对象时编译器会调用构造函数,如果有继承关系,子类的构造函数会调用父类构造函数,以此类推直到调用最上级类的构造函数;
如果添加了构造函数,将不再有默认的无参数构造函数,需要自己添加;
可以有多个构造函数,每个构造函数参数不能相同,这种机制称为重载;
也就是说函数名字可以一样,只要参数的类型或数量不一样就可以;
注意重载与函数的返回类型是无关的,;
成员函数也可以重载;重载的好处是可以少给函数起名字;
2.析构函数
构造函数时用来做初始化的;
析构函数是用来做收尾清理工作的;
一个带析构函数的结构:
#include "malloc.h" struct Person { int age; int level; char* arr; //构造函数 Person() { printf("无参构造函数执行了..."); } Person(int age,int level) { printf("有参构造函数执行了..."); this->age = age; this->level = level; arr = (char*)malloc(1024); } //析构函数 ~Person() { printf("析构函数执行了..."); free(arr); arr = NULL; } //成员函数 void Print() { printf("%d-%d\n",age,level); } };
析构函数的特点:
1】只能有一个析构函数,不能重载
2】不能带任何参数
3】不能带返回值
4】主要用于清理工作
5】编译器不要求必须提供
有些结构创建的对象的成员在初始化时需要用malloc在堆中申请了内存;
malloc申请的内存在不再使用时需要主动调用free函数释放;
当对象销毁时,可以确定申请的堆内存不再使用,此时需要释内存;
可以将释放内存的free方法写在析构函数中;
析构函数在对象销毁时自动调用;
析构函数名字与类名一样,只是前面必须加~;
3.继承
比如说要创建很多个结构;例如Studen、Teacher;
如果这些结构中都有几个相同的成员;例如Student和Teacher中都有表示年龄和性别的成员;
这就意味着每次都要写重复的代码;
可以将多个结构共有的成员放在一个单独的结构中;
然后这些结构都继承这个单独的结构,即可达到简化代码的目的;
不使用继承时:
struct Person { int age; int sex; }; struct Teacher { int age; int sex; int level; int classId; }; struct Student { int age; int sex; int code; int score; };
反汇编:
使用继承时:
struct Person { int age; int sex; }; struct Teacher:Person { int level; int classId; }; struct Student:Person { int code; int score; };
反汇编分析:
可以看到,使用继承和不适用继承反汇编是完全一样的;
继承本质上就是代码的复制,也就是编译器替我们做了重复的工作;
总结:
1】什么是继承?
继承就是数据的复制
2】为什么要用继承?
减少重复代码的编写
3】Person 称为父类或者基类
4】Teacher、Student称为子类或者派生类
5】t和s可以称为对象或者实例.
6】可以用父类指针指向子类的对象.
关于父类指针指向子类对象:
如图:一个Student对象,里面有4个成员;
Student的指针,指向结构开始的地方,可以访问x、y、a、b;
Person的指针也可以指向该结构,并且不需要转换;
因为子类对象本质上就是先将父类成员复制一份,然后再添加自己特有的成员;
也就是说子类对象前两个成员x和y合起来就是父类Person的结构;
当Person类型的指针指向子类对象开始的地方时,访问x和y完全没问题;
但在编译器看来Person类型的指针并不认识a和b无法直接访问子类对象的特有成员;
如果子类指针指向父类对象则是不可以的,需要强转;
因为子类指针表示的范围可能比父类大;
4.多层继承
struct X { int a; int b; }; struct Y:X { int c; int d; }; struct Z:Y { int e; int f; };
可以用x的指针指向z的对象;
z的对象中包含x和y的成员;
如果有一种情况:子类某个成员和父类的成员名字一样时;
比如结构y中也有个成员叫a时;
此时结构z的成员数量并不会有变化;
但在使用时必须告诉编译器是哪个结构的a;例如这里用双冒号来指定是哪个范围的a;
这种重名的情况在反汇编时和不重名没有任何区别,只是需要告诉编译器谁是谁而已;
struct X { int a; int b; }; struct Y:X { int a; int d; }; struct Z:Y { int e; int f; }; Z z; z.X::a = 1; z.b = 2; z.Y::a = 3; z.d = 4; z.e = 5; z.f = 6;
5.多重继承
c++支持多继承;
这种继承比较复杂,编译器需要维护多个地址压力比较,不推荐使用;
struct X { int a; int b; }; struct Y { int c; int d; }; struct Z:X,Y { int e; int f; };
6.练习
#include "MyClass.h" #include <stdio.h> #include <stdlib.h> struct DateInfo{ //父类 int year; int month; int day; DateInfo(int year, int month, int day){ this->year = year; this->month = month; this->day = day; } DateInfo(){ year = 1970; month = 1; day = 1; } void setDay(int day){ this->day = day; } int getDay(){ return day; } void setMonth(int month){ this->month = month; } int getMonth(){ return month; } void setYear(int year){ this->year = year; } int getYear(){ return year; } }; struct TimeInfo:DateInfo{ //继承 int hour; int minute; int second; TimeInfo(int hour, int minute, int second){ this->hour = hour; this->minute = minute; this->second = second; } TimeInfo(){ hour = minute = year = 0; } void setHour(int hour){ this->hour = hour; } int getHour(){ return hour; } void setMinute(int minute){ this->minute = minute; } int getMinute(){ return minute; } void setSecond(int second){ this->second = second; } int getSecond(){ return second; } }; void testTime(){ TimeInfo time(11,12,13); DateInfo* date = &time; //父类指针指向子类对象 printf("时间:%d/%d/%d\t%d:%d:%d\n",time.getYear(),time.getMonth(),time.getDay(),time.getHour(),time.getMinute(),time.getSecond()); printf("父类指针指向子类对象:%d/%d/%d\n",date->year, date->month, date->day); } struct MyString{ char* str; MyString(){ //无参构造 str = (char*)malloc(1024); } MyString(char* str){ int i = 0; while(*(str+i)){ //直到字符串结束符号0 i++; } this->str = (char*)malloc(i+1); for(int j =0;j<i+1;j++){ *(this->str + j) = *(str + j); } } ~MyString(){ printf("调用析构函数,释放内存空间\n"); free(str); } }; void testMyString(){ MyString str("Hello World"); printf("%s\n", str.str); } void main(){ //testTime(); testMyString(); getchar(); }