C++ 类和对象(中)构造函数 和 析构函数

news2024/12/24 8:35:46

 上篇链接:C++ 类和对象(上)_chihiro1122的博客-CSDN博客

类的6个默认成员函数

 我们在C当中,在写一些函数的时候,比如在栈的例子:

如上述例子,用C++ 返回这个栈是否为空,直接返回的话,这栈空间没有被释放,就会内存泄漏,如果我们直接释放这个栈,那么我们就拿不到这个栈是否为空的 bool 类型值。那么我们在C中就是用 一个 bool 类型变量来接收这函数返回的布尔值 ,然后再释放,最后返回这个 bool 类型变量的值。

这样做非常的麻烦,C++的祖师爷肯定也想到的这些,这就有了类当中的  6 个默认成员函数。

 我们在 上篇中提到了 空类,那么空类当中真的是什么都不存储吗,并不是。其实在空类当中,编译器会帮我们自动的去创造  6  个默认成员函数:

 其中的 构造函数和析构函数就可以帮我们自动进行 一些 如上述的初始化和 销毁的操作。

默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数。

 构造函数

构造函数是一个特殊的函数,它的名字和类的名字相同而且在创建类的类型对象的时候调用,一保证每一个成员都有一个初始值,并且,构造函数在对象的整个生命周期当中值调用一次

 特性

 需要注意的是,虽然构造函数是帮助对象当中的成员初始化,但是仅仅只是初始化,并不是给这个成员创建空间。

 其特性如下:

  • 构造函数名和类名一样
  • 没有返回值,也不需要 如 void 这样规定返回值类型
  • 实例化对象时编译器会自动的调用构造函数
  • 构造函数可以重载,也就是说我们可以创建多个构造函数。

 定义构造函数

 我们以上篇中的 栈的初始化来举例子,如果我们要用函数来实现栈的初始化的话,应该这样写:

void StackInit(Stack* ps)
{
    assert(ps);
    ps->array = (DataType*)malloc(sizeof(DataType) * 3);
    if (NULL == ps->array)
    {
        assert(0);
        return;
    }

    ps->capacity = 3;
    ps->size = 0;
}

如果我们用构造函数来实现话,就这样来实现:

class Stack
{
 public:
    Stack(int capacity = 4)
    {
            _array = (DataType*)malloc(sizeof(DataType) * 3);
            if (NULL == _array)
        {
            perror("malloc申请空间失败!!!");
            return;
        }
            _capacity = 3;
            _size = 0;
    }
};    

 我们就可以直接把在C++当中 实现的 初始化函数 copy 在这个构造函数中。

这个构造函数在创建 对象的时候就可以自动的去调用,也就是说,可以帮我们在创建对象的时候自动的去 初始化栈。这样我们在使用 这个栈的时候就不需要再去初始化一遍栈了。

 而且一般构造函数都是public的,如果是private 私有的就会报错:

当然我们上述说的是一般,也就是说不是必须的,构造函数也可以是 私有的,但是这是比较 高级的玩法。

如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦
用户显式定义编译器将不再生成。

 如果我们在类当中没有 定义 构造函数,那么编译器自动的的给我们创建一个 无参数的默认构造函数。

   在C++当中分为基本类型 / 内置类型,这些是语言本身定义的类型,如  int / double / char / 指针;自定义类型,如 用 class / struct 等等来创建的类型。

如果我们不写构造函数的定义,编译器会默认生成构造函数,内置类型不做处理,自定义类型会去调用他的默认构造:

class A
{
public:

	void Print()
	{
		cout << _a << " >>" << _b << " >> " << _c << endl;
	}

protected:
	int _a;
	int _b;
	int _c;
};

int main()
{
	A a;
	a.Print();

	return 0;
}

 输出:

 我们发现没有初始化为0,这是在C++当中的语法规定的,但是现在有些新的编译器就会把这个成员默认值初始化为 0。 像VS2019 在类当中 如果在类当中定义了自定义类型的成员,那么其中的内置类型和自定义类型都会进行处理。但是 在 VS2013  就会处理。

 也就是说,内置类型处不处理是编译器个性化的行为,但是在C++当中是规定 内置类型是不进行处理的,我们要默认认为他是不处理的。

 所以,让编译器自己生成构造函数这种操作我们是不建议的,因为指不定这个编译器既不会对 内置类型进行 初始化0,所以,如果类当中有内置成员,我们就要自己实现构造函数来初始化 类当中的 内置成员。

 如果全部都是自定义类型的成员,可以考虑让编译器自己生成。

 类当中的成员的缺省值

 注意:在C++11 当中,C++的开发团队也觉得,内置成员不处理,自定义成员处理,这样当时不妥当,所以大了一个补丁,不是把之前的语法改了,让函数在声明的时候可以给缺省值。

class A
{
public:

	void Print()
	{
		cout << _a << " >>" << _b << " >> " << _c << endl;
	}

protected:
	int _a = 1;
	int _b = 1;
	int _c = 1;
};

int main()
{
	A a;
	a.Print();

	return 0;
}

 输出:

我们发现上述就输出的是我们 缺省值。注意:此处不是初始化,是在声明的时候给的 缺省值。

 这里的 成员的 缺省值 是给编译器默认的 构造函数用的。

 因为是编译器默认使用 构造函数生成的缺省值,所以当我们在 类当中实现了 构造函数,这个缺省值就没有用了:

class A
{
public:

	A(int a, int b, int c)
	{
		_a = a;
		_b = b;
		_c = c;
	}

	void Print()
	{
		cout << _a << " >>" << _b << " >> " << _c << endl;
	}

protected:
	int _a = 1;
	int _b = 1;
	int _c = 1;
};

int main()
{
	A a(9,9,9);
	a.Print();

	return 0;
}

 输出:

 

 此时我们Print()函数就输出的是, 9  >>  9  >>  9 ,不是 1  >>  1  >>  1  了。

 构造函数重载需要注意的点

 

class A
{
public:

	A()
	{
		_a = 2;
		_b = 2;
		_c = 2;
	}

	A(int a, int b, int c)
	{
		_a = a;
		_b = b;
		_c = c;
	}

	void Print()
	{
		cout << _a << " >>" << _b << " >> " << _c << endl;
	}

protected:
	int _a = 1;
	int _b = 1;
	int _c = 1;
};

 如上述的两个构造函数就构成了函数重载,构造函数的形参,同样是可以有缺省参数的:

	A(int a = 0, int b = 0, int c = 0)
	{
		_a = a;
		_b = b;
		_c = c;
	}

 但是,构造函数中使用缺省参数会出现一些问题,如这个例子:

	A()
	{
		_a = 2;
		_b = 2;
		_c = 2;
	}

	A(int a = 0, int b = 0, int c = 0)
	{
		_a = a;
		_b = b;
		_c = c;
	}

 比如这例子,在语法上是支持的,也就是说,上述两个构造函数是支持语法的,但是在调用无参数的构造函数的时候会出现一些问题:

 我们发现此时编译器就对该调用哪一个构造函数产生了歧义。

 当然我也不能  " A a1(); " 这样写,这样写与函数声明冲突,我们在下面会讲解。

 当然,我们给一个,或者两个参数都是可以的 ,因为他不会和 无参数的构造函数冲突。

所以,我们总结一下, 其实无参数构造函数和  全缺省 构造函数都属于是 默认构造函数,再加上 我们不写编译器就会生成的 默认构造函数,这三者只能有一个,因为这三者在外部的调用都是一样的编译器不能识别。

 

 构造函数的调用
 

 构造函数的调用跟普通函数也不太一样,普通函数代用是函数名 + 参数列表构造函数调用是对象名 + 参数列表。

 像上述的例子:


int main()
{
	A a2(9,9,9);
	a2.Print();

	return 0;
}

 在类当中定义的 构造函数 是 以 类名为函数名来创建的,但是调用的时候,使用的是 创建的对象的名字。

 当这个构造函数没有参数的时候,如下:

	A()
	{
		_a = 2;
		_b = 2;
		_c = 2;
	}

 那么,就直接创建这个对象,不需要加 () 参数列表,都会自己调用 类当中  不带参数的 构造函数,如果这个例子:
 

class A
{
public:

	A()
	{
		_a = 2;
		_b = 2;
		_c = 2;
	}

	A(int a, int b, int c)
	{
		_a = a;
		_b = b;
		_c = c;
	}

	void Print()
	{
		cout << _a << " >>" << _b << " >> " << _c << endl;
	}

protected:
	int _a = 1;
	int _b = 1;
	int _c = 1;
};

int main()
{
	A a1;
	a1.Print();

	return 0;
}

 输出:

 我们发现此时输出的是,无参数 构造函数 当中初始化的值。

 注意:有参数是需要加 "()" 在 "()" 当中传参,而如果我们想调用 无参数的构造函数,那么我们是不能 加  "()"  的,如果加  "()" 就会报错:

 这是因为,如果这样写,会跟函数声明冲突,编译器不好识别。

 如上述的   " A a1(); "  是不是和 不带参数的 函数的 声明是一样,这样编译器就不好识别;但是如果是 带参数的 ,如:  " A a2(9,9,9); "  这样的就不会报错了,因为这样编译器就可识别了,因为我们在声明有参数函数的时候,参数是要加 参数的类型的: " void func ( int a, int b , int c);  " 这样的。而我们在调用有参数的构造函数的时候,是函数的传参,不需要 写 参数的类型。

 

 析构函数

 析构函数和构造函数的作用相反,它并不是完全的对  对象 进行销毁,局部对象进行销毁是由编译器来执行。所谓析构函数是对象在销毁的时候,才会执行的操作,来完成对象当中 一些成员的销毁工作。

特性

  •  析构函数的名字和类名相同,只是在最前面加上 "  ~  "   这个符号
  • 没有参数和返回值
  • 一个类只能有一个析构函数,若没有对析构函数进行定义,那么编译器会自动进行创建。注意:析构函数不能进行重载。
  • 在对象的生命周期结束的时候,编译器会自动调用析构函数。

析构函数的定义

如上篇当中的 栈的销毁:

class Stack
{
    ~Stack()
    {
        if (_array)
        {
            free(_array);
            _array = NULL;
            _capacity = 0;
            _size = 0;
        }
    }
}

如上述的~Stack() 函数就是 析构函数,他在 栈的对象销毁的时候就会自动调用。

也就是说,在上篇中提到的,内存泄漏问题,在程序运行的最后,只需要销毁 这个对象就会自动的销毁创建的栈空间。

 有了析构函数和构造函数的时候就不在怕初始化和清理函数了,也简化了。

我们上述也提到了,析构函数不写编译器也会生成默认的 析构函数,那么这个 默认的析构函数和构造函数是一样的,也是内置成员不处理,自定义成员就要按照自定义类型进行处理。

 在比如上述栈的例子,如果类当中都是 自定义类型,那么我们也可以不写析构函数,编译器自动生成的 析构函数就会帮我们 清理销毁 对象空间。

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/455188.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于OpenCV-python的图像增强和滤波

目录 彩色空间 直方图均衡化 图像滤波 梯度 一、彩色空间 OpenCV 的颜色空间主要有 BGR、HSV、Lab等&#xff0c;cvtColor 函数可以让图像在不同颜色空间转换。例如通过将花的图像转换到 HSV 颜色空间&#xff0c;在HSV空间内过滤出只含有花瓣颜色的像素&#xff0c;从而提…

公共资源包发布流程详解

文章目录 公有包发布并使用npm安装git仓库协议创建及使用 npm 私有包创建及使用 group npm 私有包私有仓账密存放位置 当公司各个系统都需要使用特定的业务模块时&#xff0c;这时候将代码抽离&#xff0c;发布到 npm 上&#xff0c;供下载安装使用&#xff0c;是个比较好的方案…

SQL Server基础 第七章 连接查询(内连接、表别名、左外连接、右外连接)

前言 连接查询是关系数据库中最主要的查询&#xff0c;主要包括内连接、外连接和交叉连接等。通过连接运算符可以实现多个表查询。前面章节的查询均是基于单表进行&#xff0c;但有时需要获取的信息存储于多张表中&#xff0c;此时就必须使用本章所介绍的多表连接查询技术来获取…

关于debug一晚上的一些思考,做开发到底要养成什么习惯?

总结&#xff1a;日志一定要写&#xff0c;日志一定要写&#xff0c;日志一定要写&#xff01; 今天晚上是我学开发过程中很不一样的一晚&#xff0c;今晚学到了很多。 虽然我也只是一个开发的初学小白&#xff0c;今天的debug分享是我的一个小方法和一个小记录&#xff0c;如…

第四章——数学知识2

欧拉函数 欧拉函数表示的是1-n中与n互质数的个数。 如1-6中&#xff1a;1&#xff0c;5都和6互质&#xff0c;因此互质数为2 欧拉函数分解质因数后表示为&#xff1a; 互质数个数可表示为 int main() {int n;cin >> n;while(n--){int a;cin >> a;//分解质因数int r…

TypeScript自学笔记

目录 1.什么是Ts? 1.1 设计公司&#xff1a;微软 1.2 TS概述 1.3 TS是静态类型 JS是动态类型 1.4 TS是强类型语言 JS是弱类型语言 2.TypeScript编译器 2.1 安装 2.2 TS自动编译和编译选项设置 3.TS的数据类型 3.1 基础数据类型number、string、boolean 3.2 Arrays&a…

大数据架构(二)大数据发展史

1.传统数仓发展史 传统数据仓库的发展史这里不展开架构细讲&#xff0c;只需快速过一遍即可。了解这个历史发展过程即可。 1.1 传统数仓历史 1.1.1 5个时代 传统数仓发展史可以称为5个时代的经典论证战。按照两位数据仓库大师 Ralph kilmball、Bill Innmon 在数据仓库建设理念上…

吃透Redis面试八股文

Redis连环40问&#xff0c;绝对够全&#xff01; Redis是什么&#xff1f; Redis&#xff08;Remote Dictionary Server&#xff09;是一个使用 C 语言编写的&#xff0c;高性能非关系型的键值对数据库。与传统数据库不同的是&#xff0c;Redis 的数据是存在内存中的&#xf…

Python Review 01

1、Anaconda Installation 使用Anaconda Navigator进行python环境管理&#xff0c;使用conda进行依赖管理。 2、Use of Jupyter 将代码写入一个个cell&#xff0c;代码文件由一个个cell组成&#xff0c;书写代码时就像一行一行在写笔记&#xff0c;这就是notebook的代码编辑环…

无宿主机权限情况下,获取pod的日志文件

如果没有宿主机权限,是无法访问宿主机及里边的文件的,但是如果想获取某些文件,如日志等如何操作呢? 整体思路:通过抓包工具,抓取websocket的的信息,然后把信息处理拼接后导出即可。 1、启动抓包工具 我这里使用的是charles抓包工具 2、打开对应pod的命令行窗口 3、抓…

基于灰度图像和小波图的双模态卷积神经网络在心血管疾病分类中的应用

目录 一、研究对象和ECG记录预处理 二、机器学习和LSTM 三、将一维ECG记录转换为二维图像 四、双模态CNN模型 五、性能评估 参考文献 一、研究对象和ECG记录预处理 本研究采用Chapman大学和Shaoxing人民医院&#xff08;浙江大学医学院绍兴医院&#xff09;收集的12导联…

领导力专题︱如何培养与提升领导力

本文内容结构 一、领导力的核心技能 1、完美领导者&#xff1f; 2、认识你的组织需要什么 3、不同层面领导力共有的特征和技能 4、你的个人行为准则 5、领导风格 6、创造个人影响力 7、完善自己的领导网络 二、领导力与领导者 1、领导力与组织环境 2、领导者还是管理…

2023/4/23总结

项目&#xff1a; 做出了个人信息界面&#xff0c;通过点击头像的方式&#xff1a; 然后就是点击头像可以选择文件&#xff08;后面考虑是存储该文件到自己的文件夹还是只是加载该文件比较好&#xff09;只是能选择文件&#xff0c;写了指定文件后缀名的代码但是好像没什么用…

如何将Edge插件迁移至Google?

问题描述&#xff1a; 因为无法访问谷歌&#xff0c;无法从谷歌插件市场下载插件 第一步&#xff1a;在电脑上找到插件地址 高亮部分&#xff1a;自己电脑上的用户名【不同用户可能会有所不同】 C:\Users\star-dream\AppData\Local\Microsoft\Edge\User Data\Default\Extensi…

rust的现状和未来发展

rust现状: Stack Overflow 的开发者调研显示只有 7% 的开发者在使用 Rust&#xff0c;对比 JavaScript、Python 等语言&#xff0c;使用 Rust 的开发者占比并不高&#xff1b;但从 2016 年开始&#xff0c;Rust 每年都是开发者最爱的编程语言。 根据 JetBrains 2021 年的调研报…

SSH远程访问及控制

文章目录 1.SSH远程管理1.1 SSH的概述1.2 OpenSSH服务器1.3 sshd_ config常用选项设置1.4 SSH端口、配置文件 2.配置OpenSSH服务端2.1 更改端口号2.2 用户登录控制 3.登录验证方式3.1 密码验证3.2 密钥对验证3.3 配置密钥对验证 5.TCP Wrappers访问控制5.1 TCPWrappers机制的基…

第37讲:Python if-elif-else流程控制语句核心概念以及案例演示

文章目录 1.流程控制的概念2.Python中代码块的相关注意事项3.if流程控制语句的语法格式4.if流程控制的简单使用4.1.单分支的if流程控制语句4.2.加else语句的if流程控制4.3.多分支的if流程控制4.4.多分支if代码优化 5.对象的布尔值6.if-else条件表达式6.1.if-else条件表达式语法…

String的那些事儿

String作为我们最常用的Java类之一&#xff0c;在日常开发过程中充当着重要角色&#xff1f;那么大家真的了解String吗&#xff1f;让我们一起看看下面的问题&#xff1a; String内存结构&#xff1f;对象存储在堆上还是栈上&#xff1f;一个String有多长&#xff1f;占内存多…

享元设计模式解读

目录 问题引进 展示网站项目需求 传统方案解决网站展现项目 传统方案解决网站展现项目-问题分析 享元模式基本介绍 基本介绍 享元模式的原理类图 对类图的说明 内部状态和外部状态 享元模式解决网站展现项目 应用实例要求 思路分析和图解(类图) 代码实现 享元模式…

创建一个 vue 3 项目

vue create projectNameVue CLI v5.0.8 ? Please pick a preset: ❯ Default ([Vue 3] babel, eslint)Default ([Vue 2] babel, eslint)Manually select featuresbabel : ES2015 and beyond。Babel 默认使用一组 ES2015 语法转换器&#xff0c;允许你使用新的语法&#xff0c…