C++:类的构造函数与析构函数
目录
一.前言
二.类的构造函数
1.构造函数基本概念与语法细则
2.编译器默认生成的无参构造函数和自定义构造函数
3.构造函数的特性(可重载)
4.关于构造函数的注意事项
5.构造函数的应用示例:
三.类的拷贝构造函数
1.拷贝构造函数基本概念
2.编译器默认生成的拷贝构造函数和自定义拷贝构造函数
3.编译器默认生成的拷贝构造函数和自定义拷贝构造函数各自的适用情形
4.拷贝构造函数被调用的典型场景
5.构造函数使用时的一个陷阱
四.类的析构函数
1.析构函数基本概念与语法细则
2.编译器默认生成的析构函数和自定义析构函数
3.析构函数的应用场景
知识架构:
一.前言
类有六个默认成员函数,这六个默认成员函数即使我们不去定义它,编译器也会在编译阶段将这些函数加到类中。
类的六个默认成员函数:
本篇主要讨论的是类的构造函数,拷贝构造函数和析构函数。
二.类的构造函数
1.构造函数基本概念与语法细则
(1)构造函数是一个特殊的成员函数,函数的标识名与类名相同.
(2)构造函数可以自定义(函数名必须为类名),也可以由编译器默认生成(用户自定义后编译器不再生成)
(3)类的构造函数没有返回值(也无须标明返回值)
(4)构造函数由编译器自行调用(用户无法自行调用),当我们用定义好的类类型去创建一个类对象实例时,编译器就会自动调用该类的构造函数.
(5)在类对象实例的整个生命周期内构造函数只被调用一次。
(6)类的构造函数一般用于类成员变量的初始化。
2.编译器默认生成的无参构造函数和自定义构造函数
编译器默认生成的无参构造函数:
代码段:
class indate { private:int i;int j; };class Date { public:private:int _day;int _month;int _year;indate _subclass; 类类型成员变量 };int main() {Date a; 创建类对象实例时编译器自动调用构造函数(无参构造函数)return 0; }
注意:
(1)我们可以认为Date类中有一个叫做Date的成员函数,indate类中有一个叫做indate的成员函数.
(2)由于我们并没有在源码的Date类中显式自定义Date函数,所以编译器在编译阶段会自行在Date类中加上编译器默认生成的无参构造函数(Date())(对于indate类也一样)。
(3)上面代码段主函数中Date a;语句执行后编译器默认生成的无参构造函数就会被调用。
对于编译器默认生成的无参构造函数有以下几点需要注意:
(1).编译器默认生成的无参构造函数不会对类对象实例的内置类型成员变量(比如上述的_day,_month,_year成员)做任何处理
(2).编译器默认生成的无参构造函数会调用其类类型成员变量的无参构造函数(这里强调该类对象成员必须有无参构造函数)(比如上面代码段中Date a的构造函数Date()会去调用其成员类indate _subclass的无参构造函数)
自定义构造函数:
(1)如果我们对类的构造函数的功能有特殊需求(一般是初始化成员变量),我们也可以自定义构造函数(构造函数函数名必须和类名相同)
(2)一旦用户自定义构造函数编译器将不再另外生成构造函数(拷贝构造函数另作讨论)
比如:
#include <iostream> #include <time.h> #include <string> #include <map>using std::cout; using std::endl;class Date { public:Date(int day, int month, int year) Date就是类的自定义构造函数{_day = day;_month = month;_year = year;cout << "initclass success" << endl;}private:int _day;int _month;int _year; };int main() { Date a(1, 2, 3); 编译器自动调用构造函数(带参构造函数)return 0; }
注意:
(1)如果类中只有带参数的构造函数,则创建类对象实例时要如上图所示在对象的创建语句中加上实参列表。
(2)如果被创建的类对象中有类对象成员,编译器会在该类的自定义构造函数中加上其类对象成员的构造函数的调用指令
比如:
可以通过汇编指令观察到这一点:
3.构造函数的特性(可重载)
构造函数可以重载(非常重要):
函数重载参见:http://t.csdn.cn/KcgeK
比如:
using std::cout; using std::endl;class Date { public:Date(){;}Date(int flag) 自定义构造函数重载{cout << "reload one" << endl;}Date(int day, int month, int year){_day = day;_month = month;_year = year;cout << "reload two" << endl;}private:int _day;int _month;int _year; };int main() {Date a(1,2,3);Date b(1);return 0; }
当一个类对象实例被创建时,调用的是构造函数的哪一种重载取决于我们创建类对象时的传参方式。
4.关于构造函数的注意事项
1. 无参的构造函数和全缺省的构造函数这类可以无参调用的构造函数在类的定义中只能存在一个重载。
关于缺省参数:http://t.csdn.cn/XubVT
比如:
2.自定义构造函数时,要定义一个可以无参调用的重载函数,不然可能会出现如下情形:
还可能出现如下情形:
也就是说类对象作为其他类的成员被创建时必须调用其无参构造函数
5.构造函数的应用示例:
在使用两个栈来实现一个队列的实际场景中,类的构造函数就能起到明显的作用
栈对象的类class stack {stack() 自定义一个stack的构造函数来初始化维护栈的指针{_data=nullptr;_capacity=0;_nums=0;} public:void stackinit(int capacity=4); 栈对象方法(函数体省略) void stackpush();void stackcheck();int& stacktop();void stackdestroy(); private:int* _data;int _capacity;int _nums;};队列对象的类class Queen {stack _pushST; 栈对象成员变量stack _popST;};int main () {Queen myqueen;return 0; }
该例中,构造函数不仅可以使源代码变得简介,还很好地避免了忘记初始化栈对象指针的情况。
三.类的拷贝构造函数
1.拷贝构造函数基本概念
类的拷贝构造函数本质上是类的构造函数的一个特殊重载函数,其形参表中只有单个形参(形参表是区分重载的标志),该形参为本类类型对象的引用(一般常用const修饰)。
关于引用:http://t.csdn.cn/oVhTv
(1)类的拷贝构造函数和类的构造函数的关系:
(2)当我们没有显式自定义类的拷贝构造函数时(即便定义了其他重载形式的构造函数),编译器在编译阶段会自动生成编译器默认的拷贝构造函数。
(3)拷贝构造函数可以自定义(函数名必须为类名,形参表中只有一个形参且必须是该类类型对象的引用),一旦用户自定义了拷贝构造函数,编译器便不会再自动生成。
(4)类的拷贝构造函数在用已存在的类对象初始化新的类对象时使用。其功能是将一个对象的内容拷贝到另一个新创建对象中去
当一个类对象实例被创建时,调用的是构造函数的哪一种重载取决于我们创建类对象时的传参方式。
比如:
可以认为编译器为Date类自动添加了一个声明为Date(const Date& object)的函数
2.编译器默认生成的拷贝构造函数和自定义拷贝构造函数
编译器默认生成的拷贝构造函数:
编译器默认生成的拷贝构造函数的功能是:对于内置类型成员变量,则逐个字节地将引用对象(实参)的成员变量拷贝到新创建的同类对象中,对于类类型成员变量,是调用该成员自身的拷贝构造函数来完成拷贝。这种拷贝方式称为浅拷贝。
比如:
如果类对象中有类类型成员变量,比如:
自定义拷贝构造函数:
class Date { public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}Date(const Date& d) 自定义拷贝构造函数{_year = d._year;_month = d._month;_day = d._day;} private:int _year;int _month;int _day; };int main() {Date d1;Date d2(d1);return 0; }
3.编译器默认生成的拷贝构造函数和自定义拷贝构造函数各自的适用情形
在一些情况下,使用编译器默认生成的拷贝构造函数是比较方便的。
但是在另外一些情况下是不能使用编译器默认生成的拷贝构造函数的,比如:用一个栈对象去初始化另外一个栈对象。
编译器默认生成的拷贝构造函数是按逐字节拷贝成员变量的浅拷贝方式进行对象拷贝的,而栈对象会在内存堆区上申请空间,上面情形中b对象和a对象都会去维护同一块堆区内存空间
两个栈对象维护同一块堆区内存空间是十分危险的,很容易出bug
因此类对象调用了其他内存资源,编译器默认生成的拷贝构造函数是不适用的,这时只能考虑使用自定义的拷贝构造函数。(用户自行设计深拷贝来完成对象的拷贝构造)
4.拷贝构造函数被调用的典型场景
(1)使用已存在对象创建新对象时
(2)调用的函数参数类型为类类型对象时
(3)函数返回值类型为类类型对象时class Date { public:Date(int year, int minute, int day){cout << "Date(int,int,int):" << this << endl;}Date(const Date& d){cout << "Date(const Date& d):" << this << endl;}~Date(){cout << "~Date():" << this << endl;} private:int _year;int _month;int _day; };Date Test(Date d) {Date temp(d);return temp; } int main() {Date d1(2022,1,13);Test(d1);return 0; }
5.构造函数使用时的一个陷阱
自定义构造函数时不能将类自身类型变量作为构造函数的形参
比如:
上面代码段中,创建d1对象后,用d1去初始化d2,但是Date的构造函数形参也是一个Date对象,调用函数时要在栈区上为函数形参开辟内存空间,相当于又创建了一个Date对象,这时又会调用形参对象的拷贝构造函数,如此往复,形成死递归。
四.类的析构函数
1.析构函数基本概念与语法细则
(1)析构函数是一个特殊的成员函数,函数的标识名是:~ + 类名.
(2)析构函数可以自定义(函数名必须为~ + 类名),也可以由编译器默认生成(用户自定义后编译器不再生成)
(3)类的析构函数没有返回值(也无须标明返回值)
(4)析构函数在类对象生命周期结束时由编译器自行调用(用户也可以显式调用)
(5)类的析构函数一般用于类成员变量内存的清理。
析构函数不能重载 !!!!!!!!
2.编译器默认生成的析构函数和自定义析构函数
编译器默认生成的析构函数:
对于类对象的内置类型成员变量不会做任何处理,对于类对象的类类型成员变量则去调用其成员自身的析构函数。
比如:
自定义析构函数以及析构函数的引用场景:
可以通过汇编观察自定义析构函数的调用过程:
3.析构函数的应用场景
typedef int DataType; class Stack { public:Stack(size_t capacity = 3){_array = (DataType*)malloc(sizeof(DataType) * capacity); 为栈数据申请堆区空间if (NULL == _array){perror("malloc申请空间失败!!!");return;}_capacity = capacity;_size = 0;}void Push(DataType data){// CheckCapacity();_array[_size] = data;_size++;}~Stack() 自定义析构函数{if (_array) {free(_array); 释放栈对象申请的堆区资源_array = NULL;_capacity = 0;_size = 0;} } private:DataType* _array;int _capacity;int _size; };int main() {Stack a;return 0; a的生命周期结束,调用析构函数 }
当对象申请了其他内存资源时,对象销毁前必须先释放其申请的内存资源(否则会造成内存泄漏),这时候析构函数的作用就可以体现出来了,我们可以自定义析构函数去释放对象所申请的内存资源,对象销毁时编译器会自动调用析构函数,使用起来非常方便。(注意由于编译器默认生成的析构函数无法进行堆区内存资源的释放,因此本例中不能使用编译器默认生成的析构函数)
C++:类的构造函数与析构函数相关推荐
- c++派生类和基类的构造函数和析构函数
基类和派生类的构造函数顺序,先调用基类的构造函数,在调用派生类的构造函数, 析构函数而言,先调用派生类的析构函数,再调用基类的析构函数 #include<iostream> using n ...
- php 派生类 构造,C++派生类的构造函数和析构函数
派生类对象中包含基类对象,因此派生类对象在创建时,除了要调用自身的构造函数进行初始化外,还要调用基类的构造函数初始化其包含的基类对象.因此,程序中任何能够生成派生类对象的语句,都要说明其包含的基类对象 ...
- 类中构造函数、析构函数与赋值函数的重写
类中构造函数.析构函数与赋值函数的重写 class String { public: String(const char *str = NULL); // 普通构造函数 String(const St ...
- C++派生类的构造函数和析构函数
C++派生类的构造函数和析构函数 派生类的构造函数和析构函数 #include <iostream> using namespace std; class student { public ...
- C++类的构造函数、析构函数与赋值函数
C++类的构造函数.析构函数与赋值函数 构造函数.析构函数与赋值函数是每个类最基本的函数.它们太普通以致让人容易麻痹大意,其实这些貌似简单的函数就象没有顶盖的下水道那样危险. 每个类只有一个析构函数和 ...
- 实验名称: 类的构造函数、析构函数和友员成员应用
实验四 (实验课时:2 实验性质:设计) 实验名称: 类的构造函数.析构函数和友员成员应用 实验目的: (1)练习类的构造函数.析构函数的定义和使用方法: (2)练习对象数组.对象指针的使用: ( ...
- c++, 派生类的构造函数和析构函数 , [ 以及operator=不能被继承 or Not的探讨]
说明:文章中关于operator=实现的示例,从语法上是对的,但逻辑和习惯上都是错误的. 参见另一篇专门探究operator=的文章:<c++,operator=>http://www.c ...
- C++中基类与派生类的构造函数和析构函数
1.Cpp中的基类与派生类的构造函数 基类的成员函数可以被继承,可以通过派生类的对象访问,但这仅仅指的是普通的成员函数,类的构造函数不能被继承.构造函数不能被继承是有道理的,因为即使继承了,它的名字和 ...
- php中类的构造函数是,PHP 类的构造函数和析构函数
通常一提到 PHP 的构造函数和析构函数,就会想到两个单词 construct 和 destruct,构造和解构. 在学习 PHP 的时候,了解到的构造函数和析构函数就是 __construct() ...
- 类的构造函数和析构函数详解
一:类的构造函数 1.构造函数定义 类的构造函数是类的一种特殊的成员函数,它会在每次创建类的新对象时执行. 构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回 void.构造函数可 ...
最新文章
- 外包程序员是如何花 5 年时间从月薪 4 千到年薪近百万?
- python 类-如何理解python的类与对象?
- 重启大法好!线上常见问题排查手册
- 周期三角波傅里叶级数例题_傅里叶详解之傅里叶级数
- Apache Hadoop 启动报错:masternode:ssh: connect to host master port 22: Connection timed out 总结
- mysql和sqlite3 ios_iOS开发:用SQLite3存储和读取数据
- 索引 mysql_深入理解MySQL索引
- javaweb 获取服务器整个文件夹下的文件_详细得不要不要的 JavaWeb快速入门,值得收藏!...
- Python基础之变量和常量
- 十九、FPGrowth算法介绍
- 慕课软件质量保证与测试(第八章.软件评审概述)
- 2021-03-04
- 【分享】小米MIUI免root一键删除系统内置软件
- 华为搜索引擎Petal与Google的区别
- 通过Vue解决跨域问题(proxy配置代理)
- 华为开源自研AI框架昇思MindSpore CPU-Ubuntu版本 Pip自动安装教程
- outlook怎么删除服务器备份文件,如何彻底删除outlook的一切数据,包括账户信息和邮件...
- 【EXCEL技巧】制作一个信息查询表(仅可查看自己)
- lilypond笔记 -- Chopin Prelude G major
- 小科普 | 什么是MTBF?那MTTF、MTTD、MTTR又是啥?