类和对象(中):构造函数与析构函数——初始化和清理被自动化了
我的github:(https://github.com/xcx55/ubuntu-linux-project)
感谢各位大佬参观我的github!!
类和对象"中",主角是六大默认成员函数:用户没有显式实现、编译器会自动生成的成员函数(前四个重要:构造、析构、拷贝构造、赋值重载;后两个取地址重载了解即可,C++11 后还会再加移动构造和移动赋值)。这篇写前两个——构造和析构,它们是 C++ 把 C 时代"手动 init / destroy"自动化的开始。
学默认成员函数就两个角度:一,不写时编译器默认生成的行为是什么、满不满足需求;二,不满足时怎么自己实现。所有细节都挂在这两条上。
一、构造函数:替代 init 的自动初始化
C 语言写 Stack、Date 都得配一个 init 函数,每次创建对象后手动调一次——忘了调就是未初始化的垃圾值。C++ 设计之初就在想这个问题:C 里内置类型变量的初始化太麻烦,每次都要调 init 还要传它的指针,面向对象需要高度封装——于是有了构造函数:对象实例化时系统自动调用,保证对象一定被初始化。
特点五条:
构造函数也是成员函数,照样传 this 指针,靠它解引用访问内部成员。
和普通函数调用方式的区别
这个细节笔记里抠得很准:普通函数调用是"函数名 + 实参",构造函数是"对象名 + 实参"——编译器通过参数匹配找到该调的版本。而且这个设计还防了一个坑:
Date func(); // 这是函数声明!
Date d1(); // 这也是函数声明,不是构造对象!
无参对象不带括号(Date d1;),一带括号就跟函数声明混了。所以:
默认构造函数有三种:编译器生成的、自己写的无参、自己写的全缺省——"不传实参就能调用"的构造都叫默认构造。
三种有且只能有一个:无参和全缺省虽然构成重载,但无参调用时有歧义;而且显式定义任何一个,编译器生成的那个就没了。这就是"显式一出,隐式就没"的规矩——六大函数的共性:都有隐式版本,写了显式,隐式就没了。
编译器生成的构造,到底干什么
对成员变量分两种类型对待(C++ 把类型分为内置类型和自定义类型,注意:所有类型的指针都是内置类型,自定义类型的指针也是):
- 内置类型成员:默认生成的构造不做初始化要求——值是不是随机看编译器,等于没管;
- 自定义类型成员:会去调用这个成员自己的默认构造函数初始化;如果这个成员没有默认构造,直接报错(解法是初始化列表,后面的章节)。
所以编译器白送的构造函数半自动:自定义类型部分它管,内置类型部分它不管。行不行看你自己的类——Date 这种全是 int 的,就得自己写;Stack 里套了别的类,那些类自己会构造。
二、析构函数:防止你忘记 destroy
构造的反面是析构。注意一个容易误解的点:析构函数不是销毁对象本身——局部对象存在栈帧里,函数结束栈帧销毁它自然就没了,不用你管;析构函数完成的是对象中资源的清理释放(类比 Stack 的 Destroy:free 掉 malloc 出来的空间)。像 Date 这种没有动态资源的,严格说都不需要析构函数。
特点:函数名 = ~类名;无参数无返回值;一个类有且只能有一个析构函数(没有"重载"的余地——参数都一样怎么重载);不写编译器生成默认的。
调用时机以生命周期为标志:对象生命周期结束自动调用。
这里笔记里有句话写得很好,原样保留:
构造函数的执行是对象生命周期正式开始的核心标志——只有当构造函数完整执行完毕,一个对象才算真正"诞生",其生命周期才正式启动。析构函数则以生命周期结束为标志来销毁。
构造和析构的设计目的其实是同一句话:防止你忘记 init 和 destroy——防止内存泄漏。C 语言里"申请了忘释放"是最常见的泄漏源,C++ 用"自动调用"把它堵上了。
三、一道题的回响:成员函数不在对象里
3-14 还有道思考题,把上一篇"对象只存成员变量"推到底:
调用成员函数并不需要先解引用对象——成员函数根本不在对象里面,对象里没有函数指针。汇编层的真实指令是:编译器去类域里找到函数的声明与定义(函数地址是链接之后才有的,属于代码段),直接 call;A::func()、a.func()、p->func() 这些写法里的 . 和 -> 在汇编层面并不对应一次解引用。真正用到 &a 的地方是传 this 指针——把对象地址传进函数,函数内部解引用 this 才访问得到成员变量,也才是 this 无效时空指针崩溃的点。
一句话:表层是"对象.函数"的解引用语法,汇编层是"类域找函数 + 传 this"两条线。这也是为什么空指针调一个不访问成员的函数,有时反而不崩——汇编里压根没碰那个指针指向的内存。
串一下:构造函数 = init 的自动化(函数名即类名、可重载、自动调用)→ 默认构造三种只能留一个 → 编译器生成的版本只管自定义类型成员 → 析构函数 = destroy 的自动化(资源清理而非销毁对象,以生命周期为标志)。下一篇收掉六大函数的第三个:拷贝构造——它会解释一个更狠的问题:为什么传值传参会无穷递归。
网硕互联帮助中心



评论前必须登录!
注册