目录 Table of Contents
构造函数与析构函数
什么是构造函数
struct Sclass
{
int a;
int b;
int c;
int d;
Sclass()//构造函数
{
printf("观察这个函数\n");
}
int Plus()
{
return a+b+c+d;
}
}
大家注意看 Sclass 函数, 它没有返回类型, 也没有 void, 我们知道普通函数如果没有返回类型的话, 前面会有一个 void, 但是这个函数没有
int main(int argc, char* argv[])
{
Sclass s; //创建一个对象 s
return 0;
}
我们运行一下, 程序打印输出了 "观察这个函数"
也就是说这个函数执行了, 我们根本就没有调用它, 它也执行了.
这个函数我们称它为构造函数, 它必须与类名也就是结构体名字一样, 它不能有返回值, void 也不能写. 然后构造函数, 我没有调用它, 它就自己执行了.
我们看我创建对象处的汇编代码就知道, 编译器自动给我 call 了构造函数.
那么这个函数它主要用来做一些初始化的工作, 举个例子.
//我们当前类里面有四个成员 a b c d.
int main(int argc, char* argv[])
{
// 按照以前的方式我们是这样给它们赋值
Sclass s = (1, 2, 3, 4);
return 0;
}
但是现在不一样了, 我们可以在构造函数里面直接给它进行赋值 :
struct Sclass
{
int a;
int b;
int c;
int d;
Sclass(int a, int b, int c, int d)//给它传四个参数进来, 刚才说了这个函数前面不能有返回值, 但是它的参数, 你可以根据需要想怎么写就怎么写
{
this->a = a;
this->b = b;
this->c = c;
this->d = d;
printf("观察这个函数\n");
}
int Plus()
{
return a+b+c+d;
}
}
但是我们现在编译一下, 编译器告诉我们 : no appropriate default constructor available, 也就是它找不到一个没有参数的构造函数了.
我们看了这块的反汇编, 意思是编译器会默认找一个没有参数的函数, 但是我们加了参数, 编译器再找的时候就找不到那个函数了. 现在怎么解决这个问题呢 ?
int main(int argc, char* argv[])
{
Sclass s(1, 2, 3, 4); //你就在这里这么写, 告诉编译器不要找那个没有参数的构造函数了, 而是去找带 4 个 int 类型的构造函数
return 0;
}
但是这么写还是有个问题, 如果我创建对象的时候不想就这么赋值, 怎么办 ?
很简单, 我们再定义一个没有参数的构造函数就好
struct Sclass
{
int a;
int b;
int c;
int d;
Sclass(int a, int b, int c, int d)
{
this->a = a;
this->b = b;
this->c = c;
this->d = d;
printf("有参构造函数\n");
}
Sclass() //新定义的一个没参数的构造函数
{
printf("无参构造函数\n");
}
int Plus()
{
return a+b+c+d;
}
}
这样就好了, 并且到时候我们可以根据需求, 来告诉编译器我们是想用有参数的还是用无参数的构造函数.
我们简单总结下构造函数 :
-
与类同名并且没有返回值
-
创建对象的时候函数执行, 所以主要用来初始化操作
-
可以有多个(最好有一个不带参数的), 这个称为重载, 其它函数也可以重载
-
编译器不要求必须提供构造函数
什么是析构函数
struct Person
{
int age;
int level;
Person()
{
printf("无参构造函数执行了...\n");
}
Person(int age, int level)
{
printf("有参构造函数执行了...\n");
this->age = age;
this->level = level;
}
~Person()
{
printf("析构函数执行了...\n");
}
void Print()
{
printf("%d-%d", age, level);
}
}
它和构造函数很像, 唯一的区别就是在前面要写上 ~
符号.
它不能带任何参数, 前面也不能写 void.
它是在对象用完了, 要被销毁的时候使用. 那么对象什么时候被销毁呢, 你要看你在哪里创建的对象, 比如这里我们在 main() 函数中创建对象, 那么 main() 函数执行完的时候, 就要销毁这个对象了.
我们看汇编代码, 你 main() 在 return 0 之前要调用这个析构函数.
简单总结一下 :
-
只能有一个析构函数, 不能重载
-
不能带任何参数
-
不能带返回值
-
主要用于清理工作
-
编译器不要求必须提供析构函数
发现没有, 不管是构造函数还是析构函数, 根本就不是我们调用的, 而是编译器替我们调用的.
析构函数到底什么时候执行
1.当对象在堆栈中分配
比如说我当前对象是在 main 函数里面创建
int main(int argc, char* argv[])
{
Person p(1,2);
return 0;
}
那么这个析构函数就是在 main 函数执行完, return 之前的时候执行
2.当对象在全局区分配
那我们把这个对象放在全局的时候, 析构函数什么时候执行呢 ?
Person p(1,2);
int main(int argc, char* argv[])
{
return 0;
}
这个时候, 当前进程在退出之前会调用这个析构函数
析构函数主要用于清理工作
什么叫清理工作, 因为析构函数执行的时候, 意味着当前的对象已经用完了, 那么对象已经申请了一些空间, 这些空间就需要被释放.
我们看看下面的代码
struct Person
{
int age;
int level;
char* arr;
Person(int age, int level)
{
this->age = age;
this->level = level;
arr = (char*)malloc(1024);
}
~Person()
{
printf("析构函数执行了...\n");
free(arr);
}
// 其它函数
}
我创建了一个对象, 对象用 malloc 函数申请了一段空间. malloc 函数分配的空间是在堆中分配的, 在堆中分配的空间, 我们用完之后一定要明确告诉操作系统我们不用了, 这个时候我们通常使用 free 函数来告诉操作系统可以回收这个堆中分配的空间.
我们可以把释放堆内存的这个 free 代码写到析构函数里面.
再比方说你打开了很多文件, 你就可以把关闭文件的操作写到析构函数里面.