从0开始C++(三):构造函数与析构函数详解

目录

构造函数 

构造函数的基本使用

构造函数也支持函数重载

构造函数也支持函数参数默认值

构造初始化列表

拷贝构造函数

浅拷贝和深拷贝

析构函数

 总结

练习一下ヽ( ̄▽ ̄)ノ 


构造函数 

构造函数的基本使用

构造函数是一种特殊的成员函数,用于创建对象时初始化,写法上有以下要求:

● 函数名称必须与类名完全一样。

● 构造函数不写返回值

● 如果程序员不手动编写构造函数,编译器就会自动添加一个默认无参数的构造函数。

● 手动添加构造函数后,编译器就不会自动添加默认无参构造函数。

class Car
{
private://权限:最私有的权限string brand;   // 品牌string modle;   // 型号int weight;     // 重量
public: // 权限:最开放的权限Car(string b,string m,int w) //手动添加的构造函数{brand=b;modle=m;weight=w;}string get_brand()   //外部函数接口{return brand;}string get_modle(){return modle;}int get_weight(){return weight;} 
};
int main()
{Car *myCar = new Car; //  创建堆内存对象//cout << Car.brand << endl;  //错误,brand是私有成员 不能外部访问cont << Car.get_brand << endl; //可以使用预留的接口访问 
}

构造函数也支持函数重载

class Car
{
private://权限:最私有的权限string brand;   // 品牌string modle;   // 型号int weight;     // 重量
public: // 权限:最开放的权限Car(string b,string m,int w) // 有参构造函数{brand=b;modle=m;weight=w;}Car()    // 无参构造函数{brand="xiaomi";modle="su7";weight=1500;}string get_brand()   // 外部函数接口{return brand;}string get_modle(){return modle;}int get_weight(){return weight;} 
};
int main()
{Car *myCar = new Car; //  调用无参构造函数Car *myCar2 = new Car("HuaWei","问界","2000"); // 此时调用有参构造函数}

构造函数也支持函数参数默认值

从第一个设置默认值的变量开始,在其变量之后的所有参数都要加默认值,在他之前的变量可以不加默认值。

class Car
{
private://权限:最私有的权限string brand;   // 品牌string modle;   // 型号int weight;     // 重量
public: // 权限:最开放的权限Car(string b="xiaomi",string m="su7",int w=1500) // 全缺省时,不能和无参构造函数同时存在{brand=b;modle=m;weight=w;}//    Car()    // 无参构造函数//    {//        brand="xiaomi";//        modle="su7";//       weight=1500;//    }string get_brand()   // 外部函数接口{return brand;}string get_modle(){return modle;}int get_weight(){return weight;} 
};
int main()
{Car *myCar = new Car; //  全缺省Car *myCar2 = new Car("问界","M7","2000"); // 此时调用有参构造函数}

构造初始化列表

当构造函数的局部变量与成员变量重名时可以使用构造初始列表的方式区分,此外,使用构造初始化列表还可以给被 const 修饰的成员变量赋值。

class Car
{
private://权限:最私有的权限const string brand;   // 品牌string modle;   // 型号int weight;     // 重量
public: // 权限:最开放的权限Car(string b,string modle,int w):brand(b),modle(modle),weight(w){} // 用构造初始化列表的方式赋值string get_brand()   // 外部函数接口{return brand;}string get_modle(){return modle;}int get_weight(){return weight;} 
};
int main()
{Car *myCar = new Car("问界","M7","2000"); // 此时调用有参构造函数}

拷贝构造函数

C++的拷贝构造函数是一种特殊的成员函数,用于创建一个对象的副本。它的参数是一个对象的引用,通过这个参数可以将一个对象的值复制给另一个对象。

拷贝构造函数通常在以下情况下被调用:

1、当用一个对象初始化另一个对象时,会调用拷贝构造函数。例如:

class MyClass {
public:MyClass(const MyClass& obj) {// 拷贝构造函数的实现}
};MyClass obj1;
MyClass obj2 = obj1;  // 调用拷贝构造函数

 2、当将一个对象作为函数参数传递给函数时,会调用拷贝构造函数。例如:

void func(MyClass obj) {// 函数体
}MyClass obj;
func(obj);  // 调用拷贝构造函数

3、当函数返回一个对象时,会调用拷贝构造函数。例如:

MyClass func() {MyClass obj;// 对 obj 进行初始化和操作return obj;  // 调用拷贝构造函数
}

需要注意的是,默认情况下,C++会生成一个默认的拷贝构造函数,该函数会将对象的所有成员变量进行一一拷贝。但如果类中存在指针或其他资源,需要手动编写拷贝构造函数来处理这些资源的拷贝问题,以防止浅拷贝带来的问题。

浅拷贝和深拷贝

C++中的拷贝操作有浅拷贝和深拷贝两种方式。

浅拷贝是指拷贝对象时,只是简单地将一个对象的数据成员的值复制给另一个对象的对应数据成员,而不会复制指向动态分配内存的指针。这意味着两个指针将指向同一块内存,当其中一个对象释放这块内存时,另一个对象的指针将成为悬空指针。例如:

class MyClass {
public:int* data;MyClass(const MyClass& other) : data(other.data) {// 拷贝构造函数的实现}
};MyClass obj1;
obj1.data = new int(5);MyClass obj2 = obj1;  // 浅拷贝delete obj1.data;  // 释放内存
cout << *obj2.data;  // 可能会输出无效的值

深拷贝是指拷贝对象时,除了复制数据成员的值外,还会为每个指针成员分配一块新的内存,并将源对象的值复制到新的内存中,以确保两个对象之间的指针成员指向不同的内存块。这样即使一个对象释放了内存,另一个对象的指针仍然有效。例如:

class MyClass {
public:int* data;MyClass(const MyClass& other) : data(new int(*other.data)) {// 拷贝构造函数的实现}~MyClass() {delete data;}
};MyClass obj1;
obj1.data = new int(5);MyClass obj2 = obj1;  // 深拷贝delete obj1.data;  // 释放内存
cout << *obj2.data;  // 仍然可以正常输出

析构函数

C++中的析构函数是一种特殊的成员函数,用于在对象的生命周期结束时执行清理操作。析构函数的名称与类的名称相同,前面加上一个波浪号(~)作为前缀,没有返回类型,也没有参数。

析构函数在以下情况下被调用:

  1. 当对象的作用域结束时,例如,当对象在函数中定义并在函数结束时销毁。
  2. 当对象是另一个对象的成员,并且该对象的析构函数被调用时。
  3. 当使用delete关键字显式释放通过new关键字配分的内存时。

析构函数的主要目的是释放对象分配的资源,例如动态分配的内存、打开的文件等。它可以通过在析构函数中使用delete关键字来释放内存,或者通过关闭文件句柄等操作来释放资源。

以下是一个示例,展示了一个类的析构函数的基本用法:

class MyClass {
public:MyClass() {cout << "构造函数被调用" << endl;}~MyClass() {cout << "析构函数被调用" << endl;}
};int main() {MyClass obj;  // 创建一个对象// 在此处执行其他操作return 0;  // 对象的作用域结束,析构函数被调用
}

当对象的作用域结束时,析构函数将被自动调用,输出如下结果:

构造函数被调用
析构函数被调用

需要注意的是,如果类中使用了动态分配的内存或其他资源,在析构函数中应该对这些资源进行释放,以避免内存泄漏或资源泄漏的问题。

 总结

构造函数

析构函数

创建对象时手动调用

当对象销毁时,自动调用

函数名称是类名

函数名称是~类名

构造函数可以重载

析构函数没有参数,不能重载

用于创建对象时并初始化

用于销毁对象时释放资源

有返回值但是不写,返回值是新创建的对象

没有返回值

练习一下ヽ( ̄▽ ̄)ノ 

写一个Dog类,要求有性别、年龄和品种三个属性,属性值封装,使用构造函数传参初始化。增加函数Dog* birth(const Dog& d),在函数体内部判断d与当前狗对象的属性值,当满足以下条件时,返回新创建的狗对象:

● 两条狗的年龄2-5

● 一公一母

新创建的狗对象的属性满足以下条件:

● 年龄:1岁

● 性别:随意

● 品种:

○ 如果父母的品种一样,品种就是父母的品种

○ 如果父母的品种不一样,品种是父母品种的拼合(自己制定拼合逻辑,或者直接是父母的品种之一)

如果两条狗不能生育,返回NULL。

参考代码

#include <iostream>
#include<string.h>
#include<time.h>
using namespace std;class Dog
{
private:int sex;  //0母 1公int age;  //小于2或大于5不能生育char *variety=new char[20];public:Dog(int s,int a,char *v){if(s==3) //  性别随机{sex=time(NULL)%2;}else{sex=s;}age=a;strcpy(variety,v);}Dog* brith(const Dog &D){if(sex != D.sex){if( age < 2 || age > 5 || D.age < 2 || D.age > 5 ){return NULL;}Dog* NewDog = new Dog(3,1,D.variety);return NewDog;}return NULL;}int get_sex(){return sex;}int get_age(){return age;}char *get_variety(){return variety;}};int main()
{int sex,age;char variety[20];while(1){cout << "请输入狗A的 性别(0:母 1:公) 年龄 品种" << endl;cin >> sex >> age >> variety;Dog dogA(sex,age,variety);cout << "请输入狗B的 性别(0:母 1:公) 年龄 品种" << endl;cin >> sex >> age >> variety;Dog dogB(sex,age,variety);Dog* NewDog=dogB.brith(dogA);if(NewDog==NULL){cout << "dogA和dogB无法生育" << endl;continue;}cout << "小狗的属性:" << endl;cout << "sex:" << NewDog->get_sex() << endl;cout << "age:" << NewDog->get_age() << endl;cout << "variety:" << NewDog->get_variety() << endl;}return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HTML(17)——圆角和盒子阴影

盒子模型——圆角 作用&#xff1a;设置元素的外边框为圆角 属性名&#xff1a;border-radius 属性值&#xff1a;数字px/百分比 也可以每个角设置不同的效果&#xff0c;从左上角顺时针开始赋值&#xff0c;没有取值的角与对角取值相同。 正圆 给正方形盒子设置圆角属性…

WordPress实时搜索插件Ajax Search Lite,轻松替代默认搜索功能

WordPress自带的默认搜索功能是跳转到搜索结果页&#xff0c;如果你想要实时搜索功能&#xff0c;特别是在问答中心显示搜索功能&#xff0c;那么建议使用这个WordPress实时搜索插件Ajax Search Lite&#xff0c;它可以在文章、页面、自定义类型文章中搜索标题、内容、摘要、自…

DP:完全背包+多重背包问题

完全背包和01背包的区别就是&#xff1a;可以多次选 一、完全背包&#xff08;模版&#xff09; 【模板】完全背包_牛客题霸_牛客网 #include <iostream> #include<string.h> using namespace std; const int N1001; int n,V,w[N],v[N],dp[N][N]; //dp[i][j]表示…

队列 + 宽搜(BFS)

例题一 解法&#xff1a; 算法思路&#xff1a; 层序遍历即可~ 仅需多加⼀个变量&#xff0c;⽤来记录每⼀层结点的个数就好了。 例题二 解法&#xff08;层序遍历&#xff09;&#xff1a; 算法思路&#xff1a; 在正常的层序遍历过程中&#xff0c;我们是可以把⼀层的结点…

SpringBoot整合justauth实现多种方式的第三方登陆

目录 0.准备工作 1.引入依赖 2.yml文件 3. Controller代码 4.效果 参考 0.准备工作 你需要获取三方登陆的client-id和client-secret 以github为例 申请地址&#xff1a;Sign in to GitHub GitHub 1.引入依赖 <?xml version"1.0" encoding"UTF-8&quo…

行车记录仪文件夹“0字节”现象解析与恢复策略

一、行车记录仪文件夹“0字节”现象描述 行车记录仪作为现代驾驶中的必备设备&#xff0c;其储存的视频数据对于事故记录和取证至关重要。然而&#xff0c;有时车主们可能会遇到这样一个问题&#xff1a;行车记录仪的某个文件夹内的文件突然变成了0字节大小&#xff0c;无法正…

用于快速充电站的 AC/DC 转换器概述

电动汽车构成了未来实现可持续交通部门的有前途技术的主要部分。AC/DC 转换器是扩展和改进 EV 功能的骨干组件。本文概述了 AC/DC 转换器、充电站类型、传统两电平 (2L) AC/DC 转换器面临的问题以及使用多电平转换器 (MLC) 的重要性。 AC/DC 充电器示意图&#xff08;&#xff…

北航数据结构与程序设计图部分选填题

一、 抓两个关键信息&#xff1a;无向图&#xff0c;邻接表。无向图中&#xff0c;边&#xff08;vi&#xff0c;vj&#xff09;要在vi的链表中记录一次&#xff0c;再以&#xff08;vj&#xff0c;vi&#xff09;的形式在vj的链表中记录一次。 每个边都要记录两次&#xff0c…

Linux机器通过Docker-Compose安装Jenkins发送Allure报告

目录 一、安装Docker 二、安装Docker Compose 三、准备测试用例 四、配置docker-compose.yml 五、启动Jenkins 六、配置Jenkins和Allure插件 七、创建含pytest的Jenkins任务 八、项目结果通知 1.通过企业微信通知 2.通过邮件通知 九、配置域名DNS解析 最近小编接到一…

Ollama深度探索:AI大模型本地部署的全面教程

目录 引言一、Ollama概述1、定义与定位2、核心功能3、技术优势4、应用场景 二、安装与配置1、系统要求2、安装方法3、配置指南4、启动Ollama服务 四、快速开始1、启动Ollama2、部署运行模型3、REEST API 五、自定义模型1、定制化的必要性2、使用Modelfile定制模型3、参数调整4、…

FPGA的基础仿真项目--七段数码管设计显示学号

一、设计实验目的 1&#xff0e; 了解数码管显示模块的工作原理。 2&#xff0e; 熟悉VHDL 硬件描述语言及自顶向下的设计思想。 3&#xff0e; 掌握利用FPGA设计6位数码管扫描显示驱动电路的方法。 二、实验设备 1. PC机 2.Cyclone IV FPGA开发板 三、扫描原理 下图所…

OSPF被动接口配置(华为)

#交换设备 OSPF被动接口配置 一、基本概念 OSPF被动接口&#xff0c;也称为抑制接口&#xff0c;即将路由器某一接口配置为被动接口后&#xff0c;该接口不会再接受和发送OSPF报文 二、使用场景 在路由器与终端相近或者直接相连的一侧配置被动接口 因为OSPF会定期发送报文…

【分类讨论】899. 有序队列

本文涉及知识点 分类讨论 LeetCode899. 有序队列 给定一个字符串 s 和一个整数 k 。你可以从 s 的前 k 个字母中选择一个&#xff0c;并把它加到字符串的末尾。 返回 在应用上述步骤的任意数量的移动后&#xff0c;字典序最小的字符串 。 示例 1&#xff1a; 输入&#xff1…

数据库设计文档编写

方法1&#xff1a;使用 Navicat 生成数据库设计文档 效果 先看简单的效果图&#xff0c;如果效果合适&#xff0c;大家在进行测试使用&#xff0c;不合适直接撤退&#xff0c;也不浪费时间。 随后在docx文档中生成目标字段的表格&#xff0c;选中全部(ctrla)进行复制(ctrlc)…

《Cloud Native Data Center Networking》(云原生数据中心网络设计)读书笔记 -- 02 Clos拓扑

本章回答以下问题&#xff1a; 什么是 Clos 拓扑&#xff0c;它与“接入 - 汇聚 - 核心”拓扑有何不同?Clos 拓扑的特征是什么?Clos 拓扑对数据中心网络的影响是什么? Clos拓扑 云原生数据中心基础设施的先行者们想要构建一种支持大规模水平扩展网络。 基本的Clos拓扑如图…

AI日报|我国人工智能核心产业规模已达5784亿元!阿里通义Qwen2成斯坦福大模型榜单最强开源模型!

⭐️搜索“可信AI进展“关注公众号&#xff0c;动手做AI Agent书籍&#xff0c;限量免费赠送&#xff01;快来参与吧&#xff5e; 文章链接&#xff1a; 福利来啦&#xff01;动手做AI Agent书籍&#xff0c;限量免费赠送&#xff01; 今日热点&#xff1a; 我国人工智能企业…

【2024亲测无坑】在Centos.7虚拟机上安装Oracle 19C

目录 一、安装环境准备 1、linux虚拟机安装 2、虚拟机快照 3、空间检查&软件上传 二、Oracle软件安装 1.preinstall安装及其他配置准备 2.oracle安装 三、数据库实例的安装 1.netca——网络配置助手 2.dbca——数据库配置助手 四、ORACLE 19C 在linux centos 7上…

Windows环境部署MySQL_8.4.0 LTS的部署安装、验证连接以及卸载全过程实操手册

前言&#xff1a; 什么是 MySQL MySQL 是一个关系型数据库管理系统&#xff0c;由瑞典 MySQL AB 公司开发&#xff0c;目前属于Oracle 公司。MySQL 是一种关系型数据库管理系统&#xff0c;关系型数据库将数据保存在不同的表中&#xff0c;而不是将所有数据放在一个大仓库内&am…

【html】如何利用hbuilderX 开发一个自己的app并安装在手机上运行

引言&#xff1a; 相信大家都非常想开发一款自己的apk&#xff0c;手机应用程序&#xff0c;今天就教大家&#xff0c;如何用hbuilderX 开发一个自己的app并安装在手机上运行。 步骤讲解&#xff1a; 打开hbuilderX &#xff0c;选择新建项目 2.选择5app,想一个名字&#x…

双写一致性

双写一致性 当修改了数据库的数据也要同时更新缓存的数据&#xff0c;缓存和数据库的数据要保持一致。 注意这里是对数据库进行写操作而不是读操作&#xff0c;通常我们有两种方式完成这个写操作&#xff0c;分别是&#xff1a;先删除缓存再修改数据库 和 先修改数据库再删除…