构造函数
通过结构体创建一个对象时,需要先声明结构体变量、再给结构体每个成员赋值(也就是所谓的初始化);
如果能够在声明变量时就直接给结构成员赋值会更加方便;构造函数就是为了达到这一目的;
示例
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、编译器不要求必须提供
析构函数
有些类创建对象的成员在初始化时可能malloc一块内存,如果开辟一块内存,使用完后需要将他free,但是并不能每个函数都使用free(),只有当对象销毁时才可以确认堆内存不再使用,所以需要编译器来提供带free方法的析构函数来收尾
示例
#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、编译器不要求必须提供
继承
当需要创建多个结构或者类,里面有相同的成员,这就意味需要写很多重复的代码
可以将这些重复的成员放在一个类中,让其他需要这个类的结构来继承,以此来简化代码
示例
不使用继承
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;
};
Teacher t;
t.age = 1;
t.sex = 2;
t.level = 3;
t.classId = 4;
printf("%d\n",sizeof(t));
Student s;
s.age = 1;
s.sex = 2;
s.code = 3;
s.score = 4;
printf("%d\n",sizeof(s));
反汇编
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-795akJWN-1681133646739)(C:\Users\whl\AppData\Roaming\Typora\typora-user-images\image-20230223180203124.png)]
使用继承
struct Person
{
int age;
int sex;
};
struct Teacher:Person
{
int level;
int classId;
};
struct Student:Person
{
int code;
int score;
};
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VJ8sua3c-1681133646740)(C:\Users\whl\AppData\Roaming\Typora\typora-user-images\image-20230223180234799.png)]
可以看到继承和不继承的反汇编是一样的
继承的本质就是代码的复制,编译器替我们做了重复的工作
总结
构造函数和析构函数可以被继承,但需要注意以下几点:
- 构造函数可以被继承,但是不能继承构造函数的实现,因为构造函数是用于创建对象的,子类需要自己定义构造函数,以便创建自己的对象。在子类的构造函数中,可以通过调用父类的构造函数来初始化父类的成员变量。如果子类没有显式地调用父类的构造函数,则会默认调用父类的无参构造函数。
- 析构函数也可以被继承,但是同样不能继承析构函数的实现。当一个对象被销毁时,先调用子类的析构函数,然后再调用父类的析构函数。如果父类的析构函数是虚函数,那么在销毁子类对象时,会先调用子类的析构函数,然后再依次调用父类的析构函数。
- 如果父类的构造函数或析构函数是私有的,那么子类无法直接访问父类的构造函数或析构函数,也就无法继承。
1、什么是继承?
继承就是数据的复制
2、为什么要用继承?
减少重复代码的编写
3、Person 称为父类或者基类
4、Teacher、Student称为子类或者派生类
5、t和s可以称为对象或者实例.
6、可以用父类指针指向子类的对象.
子类访问父类需要强转,因为子类的范围比父类大,但是并不推荐这样使用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cV7WCCVM-1681133646740)(C:\Users\whl\AppData\Roaming\Typora\typora-user-images\image-20230223181126024.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HcppZ6lr-1681133646740)(C:\Users\whl\AppData\Roaming\Typora\typora-user-images\image-20230223181134913.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8idFsSHb-1681133646741)(C:\Users\whl\AppData\Roaming\Typora\typora-user-images\image-20230223180619758.png)]
Student里面有四个成员,Student的指针指向结构开始,可以访问abxy。Person的指针也只想这个结构,因为继承的本质就是将父类复制一份给子类,所以子类的亲两个成员也可以认为是父类的结构,所以用Person类型的指针指向结构开始的地方可以访问道x和y,并且不需要类型的转换,但是不能访问a,b
多层继承
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-60ryfqPF-1681133646741)(C:\Users\whl\AppData\Roaming\Typora\typora-user-images\image-20230223181244566.png)]
struct X
{
int a;
int b;
};
struct Y:X
{
int c;
int d;
};
struct Z:Y
{
int e;
int f;
};
Z z;
z.a = 1;
z.b = 2;
z.c = 3;
z.d = 4;
z.e = 5;
z.f = 6;
printf("%d\n",sizeof(z));
反汇编
mov dword ptr [ebp-18h],1
mov dword ptr [ebp-14h],2
mov dword ptr [ebp-10h],3
mov dword ptr [ebp-0Ch],4
mov dword ptr [ebp-8],5
mov dword ptr [ebp-4],6
push 18h
push offset string "%d\n" (0042201c)
call printf (004010e0)
add esp,8
z的对象包含x和y的成员
如果子类的某个成员和父类的成员重复,结构中的成员数量并不会变化,但是必须告诉编译器是哪个类的成员,使用::来显示范围
重复和不重复的反汇编没有任何的变化,只是需要告诉编译器属于哪个类而已
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;
printf("%d\n",sizeof(z));
printf("%d\n",z.X::a);
printf("%d\n",z.b);
printf("%d\n",z.Y::a);
printf("%d\n",z.d);
printf("%d\n",z.e);
printf("%d\n",z.f);
多重继承
一个子类可以有多个父类,但是并不推荐使用,需要维护多个地址
1、多重继承增加了程序的复杂度,容易出错
2、微软建议使用单继承,如果需要多重继承可以改为多层继承
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qWeWQWZp-1681133646742)(C:\Users\whl\AppData\Roaming\Typora\typora-user-images\image-20230223184431655.png)]
struct X
{
int a;
int b;
};
struct Y
{
int a;
int d;
};
struct Z:X,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;
printf("%d\n",sizeof(z));
printf("%d\n",z.X::a);
printf("%d\n",z.b);
printf("%d\n",z.Y::a);
printf("%d\n",z.d);
printf("%d\n",z.e);
printf("%d\n",z.f);
练习
1 设计一个结构 DateInfo,要求其满足下述要求。
(1) 有三个成员: int year; int month;int day;
(2) 要求有个带参数的构造函数,其参数分别为对应年、月、日。
(3) 有一个无参数的构造函数,其初始的年、月、日分别为:2015、4、2。
(4) 要求有一个成员函数实现日期的设置:SetDay(int day)
(5) 要求有一个成员函数实现日期的获取: GetDay()
(6) 要求有一个成员函数实现年份的设置: SetYear(int year)
(7) 要求有一个成员函数实现年份的获取: GetYear()
(8) 要求有一个成员函数实现月份的设置: SetMonth(int month)
(9) 要求有一个成员函数实现月份的获取: GetMonth()
#include<stdio.h>
#include<string.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;
printf("%d\n",month);
}
DateInfo()
{
this->year = 2015;
this->month = 4;
this->day = 2;
putchar(month);
}
void SetDay(int day)
{
this->day = day;
}
int GetDay()
{
return this->day;
}
void Setmonth(int month)
{
this->month = month;
}
int Getmonth()
{
return this->month;
}
void Setyear(int year)
{
this->year = year;
}
int Getyear()
{
return this->year;
}
};
int main(int argc, char* argv[])
{
DateInfo x(1,2,3);
x.SetDay(2222);
printf("%d %d\n",x.GetDay(), x.Getyear());
}
2 设计一个结构 TimeInfo,要求其满足下述要求。
(1) 该结构中包含表示时间的时、分、秒。
(2) 设置该结构中时、分、秒的函数。
(3) 获取该结构中时、分、秒的三个函数:GetHour(),GetMinute()和GetSecond()。
struct TimeInfo {
int hour;
int minute;
int second;
void Sethour(int hour)
{
this->hour = hour;
}
int Gethour()
{
return this->hour;
}
void Setminute(int minute)
{
this->minute = minute;
}
int Getminuteh()
{
//printf("%d",this->month);
return this->minute;
}
void Setsecond(int second)
{
this->second = second;
}
int Getsecond()
{
return this->second;
}
};
3 让TimeInfo继承DateInfo 分别使用DataInfo和TimeInfo的指针访问TimeInfo
对象的成员.
#include<stdio.h>
#include<string.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;
printf("%d\n", month);
}
DateInfo()
{
this->year = 2015;
this->month = 4;
this->day = 2;
putchar(month);
}
void SetDay(int day)
{
this->day = day;
}
int GetDay()
{
return this->day;
}
void Setmonth(int month)
{
this->month = month;
}
int Getmonth()
{
return this->month;
}
void Setyear(int year)
{
this->year = year;
}
int Getyear()
{
return this->year;
}
};
struct TimeInfo:DateInfo
{
int hour;
int minute;
int second;
TimeInfo()
{
this->hour = 60;
this->minute = 50;
this->second = 40;
}
void Sethour(int hour)
{
this->hour = hour;
}
int Gethour()
{
return this->hour;
}
void Setminute(int minute)
{
this->minute = minute;
}
int Getminuteh()
{
//printf("%d",this->month);
return this->minute;
}
void Setsecond(int second)
{
this->second = second;
}
int Getsecond()
{
return this->second;
}
};
void test()
{
TimeInfo time;
DateInfo* dp = &time;
printf("%d %d %d\n", dp->day, dp->month, dp->year);
TimeInfo* tp = &time;
printf("%d %d %d\n", tp->day, tp->month, tp->year);
printf("%d %d %d\n", tp->hour, tp->minute, tp->second);
}
int main(int argc, char* argv[])
{
test();
}
4 设计一个结构叫做MyString,要求该结构能够完成以下功能:
(1) 构造函数能够根据实际传入的参数分配实际存储空间;
(2) 提供一个无参的构造函数,默认分配大小为1024个字节;
(3) 析构函数释放该空间;
(4) 编写成员函数SetString,可以将一个字符串赋值给该结构;
(5) 编写成员函数PrintString,可以将该结构的内容打印到屏幕上;
(6) 编写成员函数AppendString,用于向已有的数据后面添加数据;
(7) 编写成员函数Size,用于得到当前数据的真实长度。
编写测试程序,测试这个结构。
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<malloc.h>
struct MyString
{
char* str;
MyString(int x)
{
str=(char*)malloc(x);
}
MyString()
{
str=(char*)malloc(1024);
}
~MyString()
{
free(str);
str = NULL;
}
void setString(char* arr)
{
strcpy(str, arr);
}
void printfString()
{
puts(str);
}
void AppendString(char* arr)
{
strcat(this->str, arr);
}
int Size()
{
return strlen(this->str);
}
};
void test()
{
char str[] = "abc";
char str2[] = "123";
MyString test1;
test1.setString(str);
test1.printfString();
printf("%d\n", test1.Size());
test1.AppendString(str2);
test1.printfString();
printf("%d\n", test1.Size());
}
int main(int argc, char* argv[])
{
test();
}