1.析构函数(析构器)
------------------------------------------------------------------------------------------------------------
1.作用
当对象的地址空间被释放的时候,析构函数会被自动调用
实际开发中的作用:析构函数往往用来做收尾工作,QT里面经常这样子做
例子1:
class YY
{
public:
YY()
{
打开硬件设备/文件
}
~YY()
{
关闭硬件设备/文件
}
}
例子2:
class YY
{
public:
YY()
{
p=new char[20]; //分配堆空间
}
~YY()
{
delete []p; //释放堆空间
}
private:
char *p;
}
2.语法规则
~类名()
{
析构函数的源码;
}
3.析构函数的特点
第一个:析构函数没有返回值类型,没有形参
第二个:析构函数的名字必须跟类名一模一样
第三个:如果程序员没有写析构函数,编译器会自动帮你生成默认的析构函数,该析构函数源码中什么事情都没有做
~Cat()
{
}
如果程序员写了析构函数,编译器就不会帮你生成默认的析构函数
第四个:析构函数不可以重载
========================================================================= 析构函数的用法:
#include <iostream>
using namespace std;
class Cat
{
public:
//定义一个构造函数
Cat(int _age,float _weight);
void show();
private:
int age;
float weight;
};
void Cat::show()
{
cout<<"猫的年龄是: "<<age<<endl;
cout<<"猫的体重是: "<<weight<<endl;
}
Cat::Cat(int _age,float _weight)
{
cout<<"猫的构造函数调用了"<<endl;
age=_age;
weight=_weight;
}
int main()
{
//创建猫的对象
//Cat c1; //对应的无参构造函数Cat()
Cat c2(5,20.5); //对应的带参数的构造函数Cat(int,float)
c2.show();
}
=========================================================================
2. 拷贝构造函数
----------------------------------------------------------------------------------------------------------------
1.作用
当你用一个已经存在的对象去初始化赋值给另外一个新的对象的时候,就会自动调用拷贝构造函数
例如:
int a=99;
int b=a; //定义了新的变量b,并用a去初始化赋值给b
同样的道理:
Cat c1(5);
Cat c2=c1; //定义了新的对象c2,并用c1去初始化赋值给c2
2.语法规则
类名(类对象引用)
{
源码
}
Cat(Cat &other)
{
}
=================================================================
拷贝构造函数的例子
#include <iostream>
using namespace std;
class Cat
{
public:
Cat(int _age)
{
age=_age;
cout<<"猫的带参构造函数"<<endl;
}
Cat()
{
cout<<"猫的无参构造函数"<<endl;
}
Cat(Cat &othercat)
{
cout<<"othercat地址是: "<<&othercat<<endl;
cout<<"othercat里面的age值是: "<<othercat.age<<endl;
cout<<"this is"<<this<<endl;
cout<<"猫的拷贝构造函数"<<endl;
}
~Cat()
{
cout<<"猫的析构函数"<<endl;
}
private:
int age;
};
/*
&在不同的场合表示不同意思
类型名 &引用名;
&变量名
*/
int main()
{
//创造猫的对象
Cat c1(5);
cout<<"c1的地址是: "<<&c1<<endl;
//写法一:调用拷贝构造函数
Cat c2=c1; //Cat(Cat &othercat)
//c2.Cat(c1); //辅助你理解,但是这种写法本身是不正确的
//Cat &othercat=c1;
//写法二:调用带参构造
//Cat c2(7);
//写法三:调用无参构造
//Cat c2;
//c2=c1;
}
===================================================================
3.拷贝构造函数的特点
第一:名字跟类名相同,没有返回值类型,形参有且仅有一个,要求是引用
第二:程序员没有写拷贝构造函数,编译器会自动生成一个默认的拷贝构造函数,但是这个默认的拷贝构造函数是有bug的
bug: 默认的拷贝构造函数,它遇到指针分配堆空间这种情况,它会共用同一个堆空间(不会重新分配新的堆空间)
产生bug的情况总结成公式:
class 类名
{
private:
成员变量1;
成员变量2;
。。。。。
类型 *指针名; //有指针,就要申请堆空间
}
类名 对象1;
类名 对象2=对象1; //调用默认的拷贝构造函数,bug就产生了,对象1和对象2共用同一个堆空间
4.深拷贝和浅拷贝
浅拷贝:默认的拷贝构造函数实现的就是浅拷贝
深拷贝:程序员知道浅拷贝的bug以后,自己动手写拷贝构造函数,解决了浅拷贝的bug,我们就把自己写的拷贝构造函数称为深拷贝
#include <iostream>
#include "myhead.h"
using namespace std;
class Cat
{
public:
Cat(const char *_name,int _age)
{
//分配堆空间
name=new char[20];//分配堆空间,并让成员chare *name指针指向该空间
strcpy(name,_name);
age=_age;
cout<<"猫的带参构造函数,此时申请的name指向的堆空间首地址是: "<<(int *)name<<endl;
cout<<"此时_name指向的对空间首地址是: "<<(int *)_name<<endl;
}
~Cat()
{
delete []name;
cout<<"猫的析构函数"<<endl;
}
//自定义拷贝构造函数--》实现深拷贝
Cat(Cat &other)
{
//给当前对象的指针name单独申请堆空间
this->name=new char[20];
strcpy(this->name,other.name);
this->age=other.age;
cout<<"我自己写的是深拷贝"<<endl;
}
//修改猫的属性信息的函数
void setattr(const char *newname,int newage)
{
strcpy(name,newname);
age=newage;
}
void show()
{
cout<<"当前对象地址是: "<<this<<" 名字是:"<<name<<" 年龄是:"<<age<<endl;
cout<<"name地址是:"<<(int *)name<<" age的地址是:"<<&age<<endl;
}
private:
int age;
char *name;
};
int main()
{
//创造猫的对象
Cat c1("小黄",5);
Cat c2=c1; //一定会调用拷贝构造函数,我自己没有写拷贝构造,调用编译器自动生成的
cout<<"c1地址是: "<<&c1<<endl;
cout<<"c2地址是: "<<&c2<<endl;
c1.show();
c2.show();
//我想修改c1的信息
c1.setattr("旺财",6);
cout<<"===========修改之后============="<<endl;
c1.show();
c2.show();
}
===============================================================
练习
1. 不可以使用任何C语言的库函数,只能使用string中的方法,实现
只要B字符串中出现的字符(不论大小写),把它从A字符串中剔除
比如: A字符串是 "fhdshffFHDSHF"
B字符串是 "hfdhfd"
#include <iostream>
using namespace std;
int main()
{
int i,j;
string A;
string B;
cout<<"请输入两个字符串!"<<endl;
cin>>A;
cin>>B;
cout<<"A is: "<<A<<endl;
cout<<"B is: "<<B<<endl;
//伪代码分析理清思路--》代码中使用你喜欢的符号,汉字去分析思路
for(i=0; i<B.length(); i++)
{
for(j=0; j<A.length(); j++)
{
if(B[i]==A[j] || B[i]-A[j]==32 || B[i]-A[j]==-32)
{
//删除A里面的这个字符
A.erase(j,1);
j--; //很重要,防止漏掉一些字符,跟j++相互抵消
}
}
}
//打印删除之后的结果
cout<<"=============删除之后=================="<<endl;
cout<<"A is: "<<A<<endl;
cout<<"B is: "<<B<<endl;
}
2. 定义按钮类 class Button
属性:宽
高
按钮背景颜色
要求定义不同版本的构造函数,分别初始化不同的对象
#include <iostream>
using namespace std;
class Button
{
public:
Button()
{
w=0;
h=0;
color=0;
cout<<"无参构造函数"<<endl;
}
Button(int _w,int _h,int _color)
{
w=_w;
h=_w;
color=_color;
cout<<"带参构造"<<endl;
}
/*
Button(int w,int h,int color) //可读性太差
{
w=w;
h=h;
color=color;
} */
private:
int w;
int h;
int color;
};
int main()
{
Button b;
Button b1(100,50,0xffffff);
}