第六章 类与对象(4)析构函数与构造

析构函数(自动调用)

对象***生存期结束时***,需要做清理工作,比如:释放成员(指针)所占有的存储空间。析构函数可以完成上述工作。
 作用:用于完成对象被删除前的一些清理工作。至于完成怎样的清理工
作,由设计者在函数体中实现.  在对象的生存期结束的时刻,即在删除一个对象前由系统自动调用,然后再释放此对象所属的空间。
 设计者希望在最后一次使用对象之后所执行的任何操作都可以放在析构
函数中执行。
 如果程序中未声明析构函数,编译器将自动产生一个默认的析构函数。
 规定:
 是类的公有函数成员,名称由***类名前加” ~”构成***
没有参数,没有返回值
 一个类中只能定义一个析构函数,不能重载。

默认析构函数

若没有显式定义析构函数,则系统自动生成一个默认形式的析构函数。
系统自动生成的默认构造函数形式如下:
类名::~类名(){}
一般情况下,可以不定义析构函数
但如果类的数据成员中包含指针变量是从堆上进行存储空间
分配的话,需要在析构函数中进行存储空间的回收。

#include <iostream>
#include <string>
using namespace std;
class Test
{
private:
int a;
char *str;
public:
Test(int b, char *s) //构造函数
{ a=b; str=new char[strlen(s)+1]; strcpy(str,s); }
void setA(int b) {a=b; }
void setStr(char *s) { strcpy(str,s); }
void show () {cout<<a<<","<<str<<endl; }
Test(const Test& C) {
a=C.a; str=new char[strlen(C.str)+1];
strcpy(str,C.str); }
~Test(){delete str; cout<<"析构函数"<<endl; } //析构函数,没有结束时候a没有释放可以加上输出a然后知道谁先析构
};
int main()
{
Test xx(100,"hello");
Test yy(xx);
xx.show(); yy.show();
xx.setA(80); xx.setStr("abc");
xx.show(); yy.show();
return 0;
}
类的数据成员中包含指针变量str是从堆上进行存储空间分配,需要在析构函数中进行存储空间的回收。

可以说明析构函数被调用
在这里插入图片描述
这里出现了两个析构函数?
因为有俩对象
析构函数不管是不是深复制还是潜复制,跟着对象的,而申请了存储空间的new是要delete释放空间,析构可以用默认,申请了存储空间不能用默认要自己来写析构要加上delete,不管申请没申请空间只要有对象就要析构。
没有return 0可以到 ***“}”***时候会释放,遇到return之后也会没了
生存期结束不可以delete 都没有代码了,再说new和delete是一对

调用构造函数和析构函数的顺序

一般情况下,对同一存储类别的对象,调用析构函数的次序和调用构造函数的次序相反
在这里插入图片描述

对于不同作用域和存储类别的对象

构造函数和析构函数的调用顺序
 全局对象:
构造函数在文件中所有函数执行前调用;
当main函数执行完毕或调用exit函数时(此时程序终止) ,调用析构函数。
 函数中定义的自动局部对象(例如在函数中定义对象) :
在建立对象时调用其构造函数,如果函数被多次调用,则在每次建立对象时都要调用构造函数。
在函数调用结束、对象释放时先调用析构函数。
 函数中定义的静态(static )局部对象:
第一次调用此函数建立对象时调用构造函数一次
在调用结束时对象并不释放,因此也不调用析构函数,只在main函数结束或调用exit函数结束程序时,才调用析构函数。

不同作用域和存储类别的对象构造函数和析构函数的调用顺序

#include <iostream>
#include <string>
using namespace std;
class Test
{
private:
int a;
char *str;
public:
Test(int b, char *s) //构造函数
{ a=b; str=new char[strlen(s)+1]; strcpy(str,s);
cout<<str<<" 构造函数"<<endl; }
void setA(int b) { a=b; }
void setStr(char *s) { strcpy(str,s); }//这里有点小问题,就是传的参数太长空间不够,短的可以,之前的空间就够,要是长的要删除重新建立空间(看下面)
int getA( ) {return a; }
void show ( ) {cout<<a<<","<<str<<endl; }
Test(const Test& C) { //复制构造函数
a=C.a; str=new char[strlen(C.str)+1];
strcpy (str,C.str); }
~Test( ){ cout<<str<<" 析构函数"<<endl; delete str; } //析构函数,在释放之前输出可以
};
Test a(100,"hello"); //全局对象
void f(char xx[]){ //外部函数
cout<<"function f:"<<endl;
Test x(90,xx); //函数中定义的自动局部对象
static Test y(0,"static"); //函数中定义的静态(static )局部对象
x.show( ); y.show( );
y.setA(y.getA( )+10);
}
int main()
{
Test b(a);//进行复制构造
a.show( ); b.show( );
b.setA(80); b.setStr("abc");
a.show( ); b.show( );
f("111");f("222");f("333");
return 0;
}

在这里插入图片描述
结果解释:
hello 构造函数 是先调用复制构造函数
100,hello 展示a
100,hello 展示b
100,hello 展示a
80,abc 展示b(b被改变了)
function f: 调用f一开始的输出
111 构造函数 调用了Test的构造函数先输出了111
static 构造函数 调用Tset的构造函数
90,111 输出对象x
0,static 输出对象y
111 析构函数 结束了要删除x,而y是静态(static )局部对象程序结束才删除而且下面也
不用再建立
function f: 调用f一开始的输出
222 构造函数 调用了Test的构造函数先输出了222
90,222 输出对象x
10,static 输出对象y(调用上次时候结尾加上了10)
222 析构函数 结束了要删除x
function f: 调用f一开始的输出
333 构造函数 调用了Test的构造函数先输出了333
90,333 输出对象x
20,static 输出对象y(调用上次时候结尾加上了10)
333 析构函数 结束了要删除x
abc 析构函数 主函数里对象删除
static 析构函数 静态删除
hello 析构函数 全局对象最后删除
下面是小问题:
在这里插入图片描述
在这里插入图片描述

构造函数和析构函数举例

数据成员是字符串时,可以用分别使用:字符数组,string类的对象,字符指针表示字符串。处理方法略有不同。例如:

#include <iostream>
#include <string>
using namespace std;
class CStudent
{private:
int number;
char name[20]; //字符数组
string addr; //string类的对象
char *email; //字符指针
int age;
public:
CStudent(int xh=0, char *xm="Noname", string ad="Noad",char *em="Noemail",int a=18);//只能出现一次,不能在这出现又在下面定义时候出现
CStudent(const CStudent & s); //复制构造函数 同种对象的常引用
~CStudent( ); //析构函数
void setStudent(int xh=0, char *xm="Noname", string ad="Noad",char *em="Noemail",int a=18);
void printStudent( );
int GetAge( );
};
CStudent::CStudent(int xh, char *xm, string ad,char *em,int a)
{ number = xh;
strcpy(name, xm); //字符数组
addr=ad; //string类的对象
email=new char[strlen(em)+1];strcpy(email, em); //字符指针
age = a;
}
CStudent::CStudent(const CStudent & s)
{ if(this!=&s)//this是指针对象(当前对象的),目标对象和原来对象不一样才能不能A=A不能自己chao自己作业
{
number = s.number;
strcpy(name, s.name); //字符数组
addr=s.addr; //string类的对象
email=new char[strlen(s.email)+1];strcpy(email, s.email);
//字符指针
age = s.age;
}
}
void CStudent::setStudent(int xh, char *xm, string ad,char *em, int a)
{ number = xh;
strcpy(name, xm); //字符数组
addr=ad; //string类的对象
delete []email; //字符指针
email=new char[strlen(em)+1];strcpy(email, em);
age = a; }
CStudent::~CStudent( ){
delete [ ]email; //字符指针
cout<<number<<endl; }
void CStudent::printStudent(){
cout<<number<<" "<<name<<" "<<addr<<" "<<email<<" "<<age<<endl;}
int CStudent::GetAge()
{ return age;}
int main()
{
int sum=0;
CStudent s[8] = { CStudent(10000, "AAAAAA", "shanghai", "aaa@126.com", 20),
CStudent(10001, "BBBBBB", "qinghai", "bbb@126.com",22 ),
CStudent( ),CStudent( ),//中途没有不能省略
CStudent(10004, "EEEEEE", "shangdang", "eee@126.com",18 )//后面不给可以省略
};
s[2].setStudent(10002, "CCCCCC", "weihai", "ccc@126.com",24 );
s[3].setStudent(10003, "DDDDDD", "shandong", "ccc@126.com",21 );
s[5].setStudent(10005, "FFFFFFF", "heihai", "fff@126.com",23 );
s[6].setStudent(10006, "GGGGG", "shanxi", "ggg@126.com",20 );
s[7].setStudent(10007, "HHHHH", "jiangsu", "hhhh@126.com",20 );
for(int i=0; i<8; i++)
{ sum += s[i].GetAge();
s[i].printStudent(); }
cout << sum/8 << endl;
return 0;
}

在这里插入图片描述
有一些私有数据成员,要是在主函数想用怎么用,不能改成public,在类外不能直接用,但是可以间接使用,可用set设置,调用函数不是直接使用确实是使私有数据赋值了,想单独改可以用定义函数参数就一个,可以直接用其他的不变,getage可以得到,谁调用,谁可以得到a=数据成员.getage()得,cout只是输出不能用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jdicat

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值