C++类的构造与析构特点及作用详解

目录
  • 一、类的构造函数
    • 什么是构造函数
    • 构造函数的特点
    • 构造函数的作用
  • 二、类的析构函数
    • 什么是析构函数
    • 析构函数的特点
    • 小结
    • 析构函数的作用
  • 总结
    • 构造函数
    • 析构函数

一、类的构造函数

什么是构造函数

和类具有相同名称,并且没有返回值类型的函数,就是类的构造函数

概念模糊、直接举例:

#include <stdio.h>
#include <windows.h>
struct Test
{
    Test()        // 和类具有相同的名、并且没有返回值
    {
    }
};
int main()
{
    return 0;
}

构造函数的特点

直接先来说特点吧,然后论证:

1、构造函数在定义对象的时候被调用

2、构造函数可以进行函数重载,可以有很多个

3、创建对象时默认调用的是无参构造

证明1:

构造函数在定义对象的时候被调用;

论证如下:

#include <stdio.h>
struct Test
{
	Test()
	{
		printf("你调用了构造函数\n");		// 此处下断点、如果该函数被调用肯定会停下来。
	}
};
int main()
{
	Test te;
	printf("接力\n");
	return 0;
}

我们在Test()构造的输出语句上加断点、当程序调用Test的printf肯定会停下来,这个时候我们转到反汇编,单步步过、直到函数返回之后,就能知到刚刚是在哪里调用的构造函数了

vs2010:F7编译、F5调试、ALT+8反汇编:

F10一直运行到返回:

这里编译器做了优化,可以直接看出来是在Test定义对象的时候调用了构造。

证明2:

构造函数可以进行函数重载,可以有很多个;

论证如下:

#include <stdio.h>
#include <Windows.h>
struct Test
{
	Test()
	{
		printf("你调用了构造函数\n");		// 此处下断点、如果该函数被调用肯定会停下来。
	}
	Test(int a)
	{
		printf("重载%d\n",a);
	}
	Test(int a, int b)
	{
		printf("重载%d\n",a+b);
	}
};
int main()
{
	Test te;
	Test te1(1);
	Test te2(1,1);			// 注意、调用有参的构造函数时,需要传递参数
	system("pause");
	return 0;
}

重载了两个,注意:调用有参数的构造时,需要传递参数。

运行可以通过:

证明3:

创建对象时默认调用的是无参构造;

论证如下:

#include <stdio.h>
#include <Windows.h>
struct Test
{
	Test(int a)
	{
		printf("重载%d\n",a);
	}
	Test(int a, int b)
	{
		printf("重载%d\n",a+b);
	}
};
int main()
{
	Test te;            // 普通定义对象的方式、不带参数
	system("pause");
	return 0;
}

首先我们删除无参构造,看看能否编译通过:

不可以

然后删除有参构造:

#include <stdio.h>
#include <Windows.h>
struct Test
{
	Test()
	{
		printf("你调用了构造函数\n");		// 此处下断点、如果该函数被调用肯定会停下来。
	}
};
int main()
{
	Test te;
	system("pause");
	return 0;
}

可以

全部都加上:

#include <stdio.h>
#include <Windows.h>
struct Test
{
	Test()
	{
		printf("你调用了构造函数\n");		// 此处下断点、如果该函数被调用肯定会停下来。
	}
	Test(int a)
	{
		printf("重载%d\n",a);
	}
	Test(int a, int b)
	{
		printf("重载%d\n",a+b);
	}
};
int main()
{
	Test te;
	system("pause");
	return 0;
}

运行结果:

这已经证明了,Test te;平常这样定义对象的时候,调用的是无参构造。如果需要调用有参构造,必须传入参数;

这个特点很简单、有参函数肯定要传参嘛,所以定义对象的时候肯定要传入参数啊;

但是这里建议无论什么时候写类,最好还是写上无参构造,哪怕什么都不做也尽量写上,避免不必要的麻烦。

构造函数的作用

一般用于初始化类的成员

如下:

#include <stdio.h>
#include <Windows.h>
struct Test
{
	int x;
	int y;
	int z;
	Test()
	{
	}
	Test(int x,int y,int z)						// 构造函数初始化对象
	{
		this->x = x;
		this->y = y;
		this->z = z;
	}
};
int main()
{
	Test te;
	Test te1(1,2,3);							// 定义对象并调用有参构造进行初始化
	printf("%d %d %d\n",te1.x,te1.y,te1.z);		// 输出看看是否初始化成功
	system("pause");
	return 0;
}

运行如下:

初始化成功。

二、类的析构函数

什么是析构函数

类的构造函数名前加上'~'这个符号,就是类的析构函数

概念模糊、代码如下:

#include <stdio.h>
#include <Windows.h>
struct Test
{
	Test()
	{
		printf("你调用了一次类的构造函数\n");
	}
	~Test()
	{
		printf("你调用了一次类的析构函数\n");
	}
};
int main()
{
	Test te;
	// system("pause");        // 这里就不要让程序停下来了,不然析构不了
	return 0;
}

~Test(){}就这个样子

析构函数的特点

依然直接先来说特点,然后论证:

1、析构函数不能重载、不能有参数

2、析构函数在变量声明周期结束时被调用

3、析构函数被调用分两种情况:堆栈中定义的对象、全局区中定义的对象

证明1:

析构函数不能重载、不能有参数;

编译不通过。

既然不能有参数,那重载更不可能了

证明成功。

证明2:

析构函数在变量声明周期结束时被调用;

局部变量的生命周期是在一个大括号内,即一个所处块结束。

所以:

#include <stdio.h>
#include <Windows.h>
struct Test
{
	Test()
	{
		printf("你调用了一次类的构造函数\n");
	}
	~Test()
	{
		printf("你调用了一次类的析构函数\n");
	}
};
int main()
{
	{
		Test te;
		printf("te生命周期即将结束。\n");
	}                                    // 析构应该在这里被调用
	printf("te生命周期结束。\n");
	system("pause");
	return 0;
}

运行结果如下:

断点

结果

证明成功。

证明3:

析构函数被调用分两种情况:堆栈中定义的对象、全局区中定义的对象;

已知堆栈中定义的对象(局部变量)在块语句结束之后就会被调用,那么带有return的main函数是在返回前调用析构,还是返回后呢?

代码如下:

#include <stdio.h>
#include <Windows.h>
struct Test
{
	Test()
	{
		printf("你调用了一次类的构造函数\n");
	}
	~Test()
	{
		printf("你调用了一次类的析构函数\n");			// 断点--汇编
	}
};
int main()
{
	Test te;
	// system("pause");					// 不要使用pause,不然无法返回
	return 0;
}

断点-调试-汇编:

可以看到是在函数返回前被调用的。

如果在全局区定义的对象呢?

这个问题很难说,像我一样定义两个断点就行了,如下:

#include <stdio.h>
#include <Windows.h>
struct Test
{
	Test()
	{
		printf("你调用了一次类的构造函数\n");
	}
	~Test()                                    // 断点
	{
		printf("你调用了一次类的析构函数\n");
	}
};
Test te;
int main()
{
	// system("pause");					// 不要使用pause,不然无法返回
	return 0;                           // 断点
}

运行:

发现一运行就断在了return,当我们发F10继续运行的时候,并没有直接调用析构,而是到了括号那里:

继续F10:

通过翻译,可以得知这就是进程结束时的一些收尾工作。

继续F10:

现在大概可以总结了,类的对象定义为全局变量时,是在main函数结束之后进程退出之前,调用的析构函数。

小结

当类的对象定义为局部变量时(堆栈),定义这个对象的块作用域结束时就会调用该对象的析构函数,如果在main函数这个块作用域中定义的对象,那么就是在return之前调用析构。

当类的对象定义为全局变量时(全局区),会在main函数return函数返回之后和进程结束之前,调用该对象的析构函数。

析构函数的作用

我们知道了析构函数都是在类的对象生命周期结束时被调用,那么就代表下面不会再使用到这个对象;所以析构函数一般用于一些收尾的工作,以防忘记。

比如当你使用了该对象的成员申请了内存(malloc、new等)、或者open了一些文件,那么可以在析构函数中free delete 或者close。

例如:

#include <stdio.h>
#include <Windows.h>
struct Test
{
	int x;
	char* name;
	Test()
	{
		name = (char*)malloc(sizeof(char)*20);        // 构造时动态申请
	}
	~Test()
	{
		if(this->name!=0)                             // 析构时判断是个否为空,不为空释放
		{
			free(name);
			name = 0;
		}
	}
};
int main()
{
	Test te;
	return 0;
}

这里我就不运行了,大家可以自己测试下。

总结

构造函数

1、和类具有相同名称,并且没有返回值类型的函数,就是类的构造函数

2、构造函数在定义对象的时候被调用

3、构造函数可以进行函数重载,可以有很多个

4、创建对象时默认调用的是无参构造

析构函数

1、类的构造函数名前加上'~'这个符号,就是类的析构函数

2、析构函数不能重载、不能有参数

3、析构函数在变量声明周期结束时被调用

4、析构函数被调用分两种情况:堆栈中定义的对象、全局区中定义的对象

到此这篇关于C++类的构造与析构特点及作用详解的文章就介绍到这了,更多相关C++类的构造与析构内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++继承中的对象构造与析构和赋值重载详解

    目录 一.构造/析构顺序及继承性 二.拷贝构造的继承性 三.赋值重载不具有继承性 总结 一.构造/析构顺序及继承性 class A { private: int _a; public: A(int a = 0): _a(a) { cout << "A()" << this << endl; } ~A() { cout << "~A()"<< this <<endl; } }; class B :

  • C++编程析构函数拷贝构造函数使用示例详解

    目录 构造函数 析构函数 拷贝构造之深拷贝和浅拷贝 深浅拷贝区别 首先定义一个类进行操作. class MM { public: protected: int year; string name; } 构造函数在类中默认有一个无参的构造函数 默认的构造函数为 类名(){}:这个构造函数 如果直接写了构造函数那么这个构造函数将会没有 构造函数 class MM { public: //MM() {};//无参构造函数 MM(int year, string name) :year(year), n

  • C++分析类的对象作类成员调用构造与析构函数及静态成员

    目录 类对象作为成员 静态成员 定义和分类 静态成员变量 静态成员函数 总结 类对象作为成员 C++类中的成员可以是另一个类的对象,我们称该成员为 对象成员 例如: class Phone {} class Person { Phone p: } tips:当类中成员是其他类对象时,我们称该成员为 对象成员 Person类中有对象p作为成员,Phone为对象成员,那么当创建Person对象时,Phone与Person的构造和析构的顺序是谁先谁后? 那让我们在两个类中加上一些输出语句做提示就好了,

  • C++浅析构造函数的特性

    目录 构造函数的概念 构造函数的特性 只能有一个构造函数 构造函数的概念 构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,保证每个数据成员都有一个合适的初始值,并且在对象的生命周期内只调用一次. 构造函数的特性 (1)函数名与类名相同. (2)无返回值. (3)编译器自动调用对应的构造函数. (4)构造函数可以重载. 我们这里直接举一个例子 #include<iostream> using namespace std; class Data { public:

  • 一起来学习C++的构造和析构

    目录 1.构造函数 1.1构造函数长什么样子 1.2构造函数干嘛的 1.3思考 2.析构函数 2.1析构函数长什么样子? 2.2析构函数用来干嘛?(什么时候需要自己手动写析构函数) 3.拷贝构造函数 问题 4.深浅拷贝 (1)浅拷贝:默认的拷贝构造叫做浅拷贝 (2)深拷贝:拷贝构造函数中做了new内存操作,并且做拷贝赋值的操作 5.构造和析构顺序问题 6.C++结构体 答疑: 总结 1. 构造函数 1.1 构造函数长什么样子 (1) 函数名和类名相同 (2) 没有返回值 (3) 如果不写构造函数

  • C++类与对象深入之构造函数与析构函数详解

    目录 对象的初始化和清理 一:构造函数 1.1:构造函数的特性 1.2:构造函数的分类 二:析构函数 2.1:概念 2.2:特性 三:拷贝构造函数 3.1:概念 3.2:特性 3.3:拷贝构造函数调用时机 3.4:构造函数调用规则 对象的初始化和清理 生活中我们买的电子产品都基本会有出厂设置,在某一天我们不用时候也会删除一些自己信息数据保证安全.C++中的面向对象来源于生活,每个对象也都会有初始设置以及对象销毁前的清理数据的设置. 一:构造函数 对象的初始化和清理也是两个非常重要的安全问题,一个

  • C++超详细讲解构造函数与析构函数的用法及实现

    目录 写在前面 构造函数和析构函数 语法 作用 代码实现 两大分类方式 三种调用方式 括号法 显示法 隐式转换法 正确调用拷贝构造函数 正常调用 值传递的方式给函数参数传值 值传递方式返回局部对象 构造函数的调用规则 总结 写在前面 上一节解决了类与对象封装的问题,这一节就是对象的初始化和清理的构造函数与析构函数的内容了:对象的初始化和清理也是两个非常重要的安全问题:一个对象或者变量没有初始状态,对其使用后果是未知,同样的使用完一个对象或变量,没有及时清理,也会造成一定的安全问题:c++利用了构

  • C++构造析构赋值运算函数应用详解

    目录 了解C++默默编写哪些函数 不想使用编译器函数 为多态基类声明virtual析构函数 别让异常逃离析构函数 绝不在构造和析构过程中调用virtual函数 令operator=返回一个reference to *this 在operator=中处理自我赋值 复制对象时别忘了每个成分 了解C++默默编写哪些函数 当实现一个空类,c++会为你补上构造函数,拷贝构造函数,拷贝赋值运算符,析构函数 class Empty{}; //等于你写了 class{ public: Empty(){...};

  • C++分析构造函数与析造函数的特点梳理

    目录 构造函数的调用 构造函数的分类及调用 拷贝构造的调用时机 深拷贝与浅拷贝 构造函数的调用 默认情况下编译器至少给一个类添加3个函数 1.默认构造函数(无参,函数体实现)--完成对象的初始化 2.默认析构函数(无参,函数体为空)--完成对象的清理 3.默认拷贝构造函数,属性进行值拷贝 规则: 如果用户定义了有参构造,c++不会提供无参构造,但是提供默认拷贝构造 如果用户定义了拷贝构造函数,c++不会在提供其他函数 类名(){} 构造函数的语法 1,没有返回值,也不写void: 2,函数名称与

  • C++类的构造与析构特点及作用详解

    目录 一.类的构造函数 什么是构造函数 构造函数的特点 构造函数的作用 二.类的析构函数 什么是析构函数 析构函数的特点 小结 析构函数的作用 总结 构造函数 析构函数 一.类的构造函数 什么是构造函数 和类具有相同名称,并且没有返回值类型的函数,就是类的构造函数 概念模糊.直接举例: #include <stdio.h> #include <windows.h> struct Test { Test() // 和类具有相同的名.并且没有返回值 { } }; int main()

  • C++关于类结构体大小和构造顺序,析构顺序的测试详解

    目录 总结 #include <iostream> using namespace std; /** 1. c++的类中成员若不加修饰符的话,默认是private 2. 调用构造函数时,先递归调用最顶级的父类构造函数,再依次到子类的构造函数. 3. 调用析构函数时相反,先调用最底层的子类析构函数,再依次到父类的构造函数. 4. 空类的sizeof(A)大小为1,多个空类继承后的子类大小也是1 */ class A{ public: A() { cout<<"A const

  • C++类中六个默认的成员函数详解

    浅谈 先来说一下"this指针": C++中通过引入this指针解决该问题,暨:C++编译器给每个"非静态的成员函数"增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有成员变量的操作,都是通过该指针去访问,只不过所有的操作对用户是透明的,暨用户不需要来传递,编译器自动完成. 说了这么多其实编译器在生成程序时获取对象首地址的信息.然后将获取的对象的首地址存放在了寄存器中,成员函数的其它参数都是存放在栈中.而this指针参数则是

  • Android项目中实体类entity的作用详解

    估计很多入门安卓的朋友对entity很困惑,为什么要写实体类?有什么用?写来干什么? 对于实体类的理解我入门的时候也是困惑了好久,后面用多了才慢慢理解,这篇博客就当复习和笔记. Java中entity(实体类)的写法规范 在日常的Java项目开发中,entity(实体类)是必不可少的,它们一般都有很多的属性,并有相应的setter和getter方法.entity(实体类)的作用一般是和数据表做映射.所以快速写出规范的entity(实体类)是java开发中一项必不可少的技能. 在项目中写实体类一般

  • PHP基于phpqrcode类生成二维码的方法示例详解

    HP QR Code是一个PHP二维码生成类库,利用它可以轻松生成二维码,官网提供了下载和多个演示demo,查看地址: http://phpqrcode.sourceforge.net/ 下载官网提供的类库后,只需要使用phpqrcode.php就可以生成二维码了,当然您的PHP环境必须开启支持GD2. phpqrcode.php提供了一个关键的png()方法,其中 参数$text表示生成二位的的信息文本: 参数$outfile表示是否输出二维码图片 文件,默认否: 参数$level表示容错率,

  • OpenHarmony实现类Android短信验证码及倒计时流程详解

    目录 1.背景 2.效果预览 3.思路 4.创建应用 5.删除原有代码 6.编写代码实现功能 1.布局拆分 2.实现堆叠布局 3.实现文本展示 4.实现输入框 5.实现短信验证码按钮 6.定时器的实现 7.签名及真机调试 8.源码地址 9.总结 1.背景 倒计时的效果在网站或其他平台看到的很多了吧,今天就让我们来看看在OpenHarmony中如何实现它吧! 2.效果预览 视频效果演示 传送门 开发板:DAYU200 IDE:DevEco Studio 3.0 Release Build Vers

  • Spring boot工具类静态属性注入及多环境配置详解

    由于需要访问MongoDB,但是本地开发环境不能直接连接MongoDB,需要通过SecureCRT使用127.0.0.2本地IP代理.但是程序部署到线上生产环境后,是可以直接访问MongoDB的,因此开发好程序后,总是要修改一下MongoDB服务器的IP才能提交代码,这样很是不方便. private static final String PUBCHAT_HOST = "127.0.0.2"; // private static final String PUBCHAT_HOST =

  • Javascript定义类(class)的三种方法详解

    将近20年前,Javascript诞生的时候,只是一种简单的网页脚本语言.如果你忘了填写用户名,它就跳出一个警告. 如今,它变得几乎无所不能,从前端到后端,有着各种匪夷所思的用途.程序员用它完成越来越庞大的项目. Javascript代码的复杂度也直线上升.单个网页包含10000行Javascript代码,早就司空见惯.2010年,一个工程师透露,Gmail的代码长度是443000行! 编写和维护如此复杂的代码,必须使用模块化策略.目前,业界的主流做法是采用"面向对象编程".因此,Ja

随机推荐