#构造函数、析构函数、继承

文章介绍了C++中的构造函数和析构函数的概念,包括它们的特点、作用以及与对象初始化、内存管理的关系。同时,文章阐述了继承的基本原理,展示了如何通过继承减少代码重复,并探讨了多层继承和多重继承的情况。最后,给出了几个关于日期、时间和自定义字符串结构的设计练习。
摘要由CSDN通过智能技术生成

构造函数

通过结构体创建一个对象时,需要先声明结构体变量、再给结构体每个成员赋值(也就是所谓的初始化);

如果能够在声明变量时就直接给结构成员赋值会更加方便;构造函数就是为了达到这一目的;

示例
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. 如果父类的构造函数或析构函数是私有的,那么子类无法直接访问父类的构造函数或析构函数,也就无法继承。

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();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值