c++类与对象三

news2024/11/23 13:14:33

C++类与对象三

上期我们介绍了类的实例化,大小计算,还有this指针。这期我们继续深入更高层次的用法

类的六个默认函数

如个一个类里面没有成员,就是空类,但是空类里面真的什么都没有吗,并不是,在编译器中,会默认生成六个默认成员函数,但是这只是隐式生成的,用户是看不见的。

默认成员函数:用户没有显示显现,编译器自动生成的成员函数称为默认成员函数

image-20241003115447369

VS2022自动生成的默认成员函数

image-20241011164552187

六个默认函数主要功能

构造函数

概念

class Date {
public:
	void Init(int year,int month,int day) {
		_year = year;
		_month = month;
		_day = day;
	}
	void Print() {
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _day;
	int _month;
	int _year;

};

int main() {
	Date d1;
	d1.Init(2011, 02, 05);
	d1.Print();

	Date d2;
	d2.Init(2024, 10, 10);
	d2.Print();
}

对于以上函数,我们对d1d2对象初始化时,都可以调用Init函数来初始化赋值,但是每次创建一个对象进行初始化,就必须调用一次Init函数,非常的不方便。有没有一种函数,在创建对象时,默认帮你完成初始化呢?

构造函数是一个特殊的成员函数名字和类相同,创建类类型对象时由编译器自动调用,以保证每个数据成员都有一个合适的初始值,并且在对象整个生命周期内只调用一次

注意是**自动调用!!!**极大节省调用函数时的燃烧的卡路里

特性

构造函数是特殊的成员函数,虽然名字叫构造,但是主要任务不是开空间创建对象,而是初始化对象

1、构造函数与类名相同

2、无返回值

3、对象实例化时编译器自动调用对应的构造函数

4、构造函数可以重载

主要分为两种构造方式

1、无参构造

2、有参构造

class Date {
public:
    Date() {

    }//无参构造

    Date(int year, int month, int day) {
        _year = year;
        _month = month;
        _day = day;
    }//有参构造
    
    void Print() {
        cout << _year << '-' << _month << '-' << _day << endl;
    }
private:
    int _day;
    int _month;
    int _year;
};

int main() {
    //无参构造
    Date d1;

    //有参构造
    Date d2(2014,11,11);
    d2.Print();
    
    Date d3();
    // 注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明
    // 以下代码的函数:声明了d3函数,该函数无参,返回一个日期类型的对象
    // warning C4930: “Date d3(void)”: 未调用原型函数(是否是有意用变量定义的?)
    
     return 0;
}

image-20241011173119480

无参和有参的区别

如果用户没有显示定义构造函数,编译器会自动生成一个无参的默认的构造函数,一旦用户显示定义编译器就不再生成。

class Date {
public:
    //Date(int year, int month, int day) {
    //    _year = year;
    //    _month = month;
    //    _day = day;
    //}//有参构造

    void Print() {
        cout << _year << '-' << _month << '-' << _day << endl;
    }

private:
    int _day;
    int _month;
    int _year;
};

int main() {
    //无参构造
    Date d1;

     return 0;
}

注释掉有参构造,可以识别到编译器默认的构造函数

放开有参构造,则编译器自动生成的构造函数会变成有参构造,因此系统无法识别无参调用

image-20241011193901798

注意事项

一、有关于系统的默认构造,有很多同学有疑惑:不实现构造函数的情况下,编译器会生成默认的构造函数。但是看起来默认构造函数又没什么用?d1对象调用了编译器生成的默认构造函数,但是d1对象_year/_month/_day,依旧是随机值。也就说在这里编译器生成的默认构造函数并没有什么用??

为了验证上面的看法,我们需要在不同环境的编译器下测试以下代码

VS2022

class Stack {
public:
	Stack(int capacity = 4)
	{
		_a = (int*)malloc(sizeof(int) * capacity);
		if (_a==nullptr) {
			perror("malloc fail!!");
		}
		_capacity = capacity;
		_top = 0;
	}

private:
	int* _a;
	int _capacity;
	int _top;
};

class MyQueue {
	Stack _push;
	Stack _pop;
	int _size;
};

int main() {
	Stack t1;
	MyQueue m1;

	return 0;
}

image-20241011203801388

我们给Stack显示写上构造函数,而MyQueue这个类是没有显示写构造函数的,是编译器默认生成的构造函数。

在监视窗口下,t1对象是显示的构造函数,m1是编译器生成的。

可以看到m1对象没有显示的构造函数,但是_size的值却变成了0!!!!

首先,C++把类型分成内置类型和自定义类型。内置类型就是语言提供的数据类型,例如int、char、double.....,自定义类型就是struct/class/union等自己定义的类型。让我们看看下面的代码

class Time {
public:
	Time() {
		cout << "Time()" << endl;
		_hour = 0;
		_minute = 0;
		_second = 0;
	}
private:
	int _hour;
	int _minute;
	int _second;
};

class Date {
private:
	int _year;
	int _month;
	int _day;
    //这三个都是内置类型

	Time _t1;//自定义类型
};

int main() {
	Date d1;

	return 0;
}

调试以上代码调试发现,C++对内置类型是不初始化的,在自定义类型中,有显示的构造函数。

image-20241012105101279

可以看见在Date中三个内置类型是随机值,而自定义类型中是用户自己写的显示构造函数。当我们注释掉Time类中的构造函数,使用系统生成的默认构造函数时

image-20241012105318196

发现**他们都成了乱码!!!**回顾前面内容

class MyQueue {
	Stack _push;
	Stack _pop;
	int _size;
};

为什么这段代码中int _size明明是内置类型,为什么又被初始化了!!!

其实最根本的原因还是VS2022的锅,这是新版本的优化!!!因此这是个巨坑。稍有不慎就会因为初始化的问题出现各种问题,当切换到VS2013调试MyQueue他就原形毕露(由于这里不方便展示,只能说出结论)

同学们到这里可能就乱了,首先我们要记住:C++对自定义类型是会初始化的,但是会调用他的默认构造函数,对内置类型,是不会初始化的!,因此VS2022这里就是一个妥妥的渣男,欺骗人感情。因此_size实际上是随机值的。

针对内置成员不初始化的问题,在C++11中,新增了一个补丁,在定义内置类型时,可以给缺省值

class Date {
private:
	int _year=1970;
	int _month=01;
	int _day=01;

	Time _t1;
};

那么这三个自定义类型是有内存的吗?答案是没有,因为这个是缺省值

二、无参的构造函数和全缺省的构造函数都被称为默认构造函数,并且默认构造函数只能有一个。注意:无参构造函数全缺省构造函数、用户没有写编译器默认生成的构造函数,都是默认构造函数

class Date {
public:
	//无参构造
	Date() {
		_year = 1970;
		_month = 01;
		_day = 01;
	}
	//有参构造
	Date(int year=1970,int month=01,int day=01) {
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main() {
	Date d1;

	return 0;
}

image-20241012111510475

以上代码会报错,因为只能有一个默认构造函数

写法

根据上面对构造函数的了解,可以给出参考写法—全缺省构造

class Date {
public:
	//有参构造
	Date(int year=1970,int month=01,int day=01) {
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main() {
	Date d1;
	Date d2(2024, 10, 10);

	return 0;
}

image-20241012111836899

一举两得!

析构函数

概念

通过前面的构造函数,我们知道一个对象是怎么来的,那么对象又是怎么没的呢。

析构函数:与构造函数功能相反,析构函数不是完成对象本身的销毁,局部变量销毁是由编译器完成的,而对象在销毁时会自动调用析构函数,完成对本对象的资源清理

特性

1、析构函数名是在类名前加上字符~

2、无参数无返回值

3、一个类只能有一个析构函数,用户没有显示定义,则编译器会自动生成默认的析构函数

4、对象生命周期结束时,C++编译器自动调用

5、析构函数不能重载

class Stack {
public:
	Stack(int capacity = 4) {
		_array = (int*)malloc(sizeof(int) * capacity);
		if (_array == nullptr) {
			perror("malloc fail");
		}

		_capacity = capacity;
		_size = 0;
	}

	void Push(int x) {
		_array[_size++] = x;
	}

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

private:
	int* _array;
	int _capacity;
	int _size; 

};

int main() {
	Stack s1;
	Stack s2;
	s1.Push(1);
	s2.Push(3);

	return 0;
}

当上面的对象生命周期结束时,会自动调用析构函数~Stack()清理内部资源

image-20241012171223489

生命周期结束资源清理

那么系统默认生成的析构函数又是怎么样的呢

class Time {
public:
	~Time() {
		cout << "~Time()" << endl;
	}
private:
	int _hour;
	int _minute;
	int _second;
};

class Date {
private:
	int _year = 1970;
	int _month = 01;
	int _day = 01;

	Time _t1;
};

int main() {
	Date d1;
	return 0;
}

运行结果是创建哪一个类就运行哪一个类的默认析构函数

这段代码一共运行了倆个析构函数,第一个是Time类显示定义的,第二个是Date系统默认生成的(注意:内置类型是由系统回收资源的,而自定义类型是需要析构函数去清理的)

因此,析构函数是非常重要的,可以在对象生命周期自动调用,清理不需要的资源,降低内存泄漏的风险

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

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

相关文章

大数据新视界 --大数据大厂之大数据环境下的零信任安全架构:构建可靠防护体系

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

Javascript 仅允许在异步函数和模块顶级使用 “await“

这个错误的原因&#xff0c;是我们在一个普通函数里调用了async函数&#xff0c;因为async 函数是异步函数&#xff0c;它返回一个 Promis&#xff0c;await必须与async一同出现。所以这里普通函数无法调用它。 async和await只是使函数调用看起来像同步而已&#xff0c;比如下…

【MMMLP】核心方法解读

此方法用于顺序推荐&#xff0c;和我的研究方向不一样&#xff0c;所以这里只探讨值得借鉴的部分 abstract&#xff1a; 现有的顺序推荐方法要么不能直接处理多模态&#xff0c;要么计算量大。为了解决这个问题&#xff0c;我们提出了一种新的多模态多层感知器&#xff08;MM…

MYSQL-windows安装配置两个或多个版本MYSQL

安装第一个mysql很简单&#xff0c;这里不再赘述。主要说说第二个怎么安装&#xff0c;服务怎么配置。 1. 从官网下载第二个MySQL并安装 一般都是免安装版了&#xff0c;下载解压到某个文件目录下(路径中尽量不要带空格或中文)&#xff0c;再新建一个my.ini文件&#xff08;或…

QGroundControl最新版本MacOS平台编译(使用CMakeLists.txt)

1.下载源码: git clone https://github.com/mavlink/qgroundcontrol.git --recursive 2.安装依赖: brew install GStreamer 设置环境变量:GST_PLUGIN_PATH 安装SDL2: brew install SDL2

C#自定义特性

特性的用处 一般用来影响某一个类的个别字段或者方法 定义特性 需要将类继承Attribute 可以通过构造函数的方式影响使用特性的方法 可以通过给自定义的特性通过加AttributeUsage特性的方法进行进一步管理 AttributeUsage特性默认传三个参数 第一个参数一般用来约束此自定义…

怎么把m4a转换成mp3?8种关于m4a转成MP3格式的转换方法

怎么把m4a转换成mp3&#xff1f;尽管m4a格式在音质上表现突出&#xff0c;但并不是所有设备和软件都能支持&#xff0c;给一些用户带来了不便。为了保证音乐文件能够在更多设备和平台上播放&#xff0c;许多人选择将m4a转换为MP3格式。MP3几乎可以在所有播放器和设备上使用&…

LeetCode讲解篇之2320. 统计放置房子的方式数

文章目录 题目描述题解思路题解代码题目链接 题目描述 题解思路 我们首先发现一个规律街道两侧是否放置房子是独立的&#xff0c;即放置房子的方式数 一侧放置房子的方式数 * 另一侧放置房子的方案数 一侧放置房子的方式数的二次方 对于一侧[0, i]范围内地块放置房子的方式…

starrocks-删除表字段

1、背景 之前做了个大宽表&#xff0c;将近100个字段&#xff0c;但是后来发现很多字段在实际生产上都没有用到&#xff0c;并且随着数据量的增加&#xff0c;给集群的存储以及消费任务的解析带来了比较大的压力。所以决定对字段做删除处理。 当前的表是使用routine load任务从…

渗透测试 之 AD域渗透 【AS-REP Roasting】 攻击技术详解

说明: AS-REP Roasting是一种对用户账户进行离线爆破的攻击方式。但是该攻击方式使用比较受限&#xff0c;因为其需要用户账户设置“不要求Kerberos预身份验证”选项&#xff0c;而该选项默是没有勾选的。Kerberos 预身份验证发生在Kerberos身份验证的第一阶段&#xff08;AS_…

14. 最长公共前缀【字符串】

文章目录 14. 最长公共前缀解题思路Go代码 14. 最长公共前缀 14. 最长公共前缀 编写一个函数来查找字符串数组中的最长公共前缀。 如果不存在公共前缀&#xff0c;返回空字符串 ""。 示例 1&#xff1a; 输入&#xff1a;strs ["flower","flow&…

STM32 QSPI接口驱动GD/W25Qxx配置简要

STM32 QSPI接口GD/W25Qxx配置简要 &#x1f4dd;本篇会具体涉及介绍Winbond&#xff08;华邦&#xff09;和GD(兆易创新) NOR flash相关型号指令差异。由于网络上可以搜索到很多相关QSPI相关知识内容&#xff0c;不对QSPI通讯协议做深度解析。 &#x1f516;首先确保所使用的ST…

2022年10月自考《数据库系统原理》04735试题

目录 一.单选题 二.填空题 三.设计题 四.简答题 五.综合题 一.单选题 1.数据库系统管理员的英文缩写是 (书中)P29页 A.Data B.DB C.DBA D.DBS 2.客户/服务器模式中&#xff0c;客户端和服务器可以同时工作在同一台计算机上&#xff0c;该方式称为 (书中)P37页 A.单机方…

使用Copilot自动在Rstudio中写代码,提高效率!

原文链接&#xff1a;使用Copilot自动在Rstudio中写代码&#xff0c;提高效率&#xff01; 2022年教程总汇 2023年教程总汇 引言 今天我们分享&#xff0c;在Rstuido中使用copilot自动写代码&#xff0c;提高你的分析和绘图效率。 copilot是2024年9月后引入到Rstuido中&…

如何在 IDEA 中导入 Java 项目的 Git 仓库并启动

目录 前言1. 从 Git 仓库导入 Java 项目2. 配置 Maven2.1 配置 Maven 仓库和设置文件2.2 加载依赖 3. 配置 Tomcat 并运行项目3.1 配置 Tomcat3.2 配置 Server URL3.3 启动项目 4. 常见问题与解决方法4.1 Maven 依赖无法下载4.2 Tomcat 部署失败4.3 项目启动后无法访问 结语 前…

从RNN讲起——序列数据处理网络

文章目录 RNN&#xff08;Recurrent Neural Network&#xff0c;循环神经网络&#xff09;1. 什么是RNN&#xff1f;2. 经典RNN的结构3. RNN的主要特点4. RNN存在问题——长期依赖&#xff08;Long-TermDependencies&#xff09;问题 LSTM&#xff08;Long Short-Term Memory&a…

使用libssh2实现多线程模式的文件上传与下载

使用libssh2实现多线程模式的文件上传与下载 一、准备工作二、初始化SSH连接三、文件上传与下载四、多线程处理五、总结libssh2 是一个开源的SSH库,用于在C/C++程序中实现SSH2协议的功能。通过libssh2,我们可以方便地进行远程登录、执行命令、上传和下载文件等操作。在多线程…

一区大黄蜂!人工蜂群算法优化!ABC-CNN-LSTM-MATT多特征分类预测

一区大黄蜂&#xff01;人工蜂群算法优化&#xff01;ABC-CNN-LSTM-MATT多特征分类预测 目录 一区大黄蜂&#xff01;人工蜂群算法优化&#xff01;ABC-CNN-LSTM-MATT多特征分类预测分类效果基本介绍程序设计参考资料 分类效果 基本介绍 1.Matlab实现ABC-CNN-LSTM-MATT人工蜂群…

c++关于内存的知识点上速成

温馨提示&#xff1a;本篇文章的内容涉及的是c内存的管理方式 c内存管理的方式 new的使用方式 类型 对象名 new 类型 注意&#xff1a;如果对象名前面的类型有星号&#xff0c;后面的类型&#xff08;new后面的&#xff09;不需要星号 样例&#xff1a; delete的使用方…

VMDK 0X80BB0005 VirtualBOX虚拟机错误处理-数据恢复——未来之窗数据恢复

打开虚拟盘文件in7.vmdk 失败. Could not get the storage format of the medium 7\win7.vmdk (VERR_NOT_SUPPORTED). 返回 代码:VBOX_E_IPRT_ERROR (0X80BB0005) 组件:MediumWrap 界面:IMedium {a a3f2dfb1} 被召者:IVirtualBox {768 cd607} 被召者 RC:VBOX_E_OBJECT_NOT_F…