c++ 构造函数与析构函数

本文参考菜鸟教程,仅作笔记用。

构造函数

构造函数(Constructor)是一种特殊的方法,用于在创建对象时进行初始化操作。构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回 void。在面向对象编程中,通常每个类都可以有一个构造函数。构造函数的作用是初始化对象的状态,为对象的属性赋予初始值,以确保对象在创建后处于一个合适的状态。

在继承关系中,子类可以继承父类的属性和方法,但是构造函数却不能直接继承。这是因为构造函数的特殊性质,它在对象创建时被调用,用于初始化该对象的状态。当子类继承父类时,子类可以使用父类的属性和方法,但是子类的构造函数需要负责初始化子类自己的属性,而不能直接继承父类的构造函数。

通常情况下,如果子类没有定义自己的构造函数,那么会隐式地调用父类的构造函数来初始化子类的实例但是如果子类定义了自己的构造函数,那么子类的构造函数就会覆盖父类的构造函数,这样父类的构造函数就无法被子类直接继承和使用了。

#include <iostream>using namespace std;class Line
{public:void setLength( double len );double getLength( void );Line();  // 这是构造函数private:double length;
};// 成员函数定义,包括构造函数
Line::Line(void)
{cout << "Object is being created" << endl;
}void Line::setLength( double len )
{length = len;
}double Line::getLength( void )
{return length;
}
// 程序的主函数
int main( )
{Line line;// 设置长度line.setLength(6.0); cout << "Length of line : " << line.getLength() <<endl;return 0;
}
<<  Object is being created
<<  Length of line : 6

带参数的构造函数

#include <iostream>using namespace std;class Line
{public:void setLength( double len );double getLength( void );Line(double len);  // 这是构造函数private:double length;
};// 成员函数定义,包括构造函数
Line::Line( double len)
{cout << "Object is being created, length = " << len << endl;length = len;
}void Line::setLength( double len )
{length = len;
}double Line::getLength( void )
{return length;
}
// 程序的主函数
int main( )
{Line line(10.0);// 获取默认设置的长度cout << "Length of line : " << line.getLength() <<endl;// 再次设置长度line.setLength(6.0); cout << "Length of line : " << line.getLength() <<endl;return 0;
}
<< Object is being created, length = 10
<< Length of line : 10
<< Length of line : 6

使用初始化列表来初始化字段

初始化列表允许我们在构造函数中以更加直接和高效的方式初始化类的成员变量,特别是对于常量或者引用类型的成员变量尤其有用。
使用初始化列表来初始化字段:

Line::Line( double len): length(len)
{cout << "Object is being created, length = " << len << endl;
}

上面的语法等同于如下语法:

Line::Line( double len)
{length = len;cout << "Object is being created, length = " << len << endl;
}

再举个例子

#include <iostream>
#include <string>class Car {
private:std::string& owner;  // Reference member variablestd::string model;public:// Constructor with initialization listCar(std::string& o, const std::string& m) : owner(o), model(m) {// Constructor body (if needed)}void displayInfo() const {std::cout << "Owner: " << owner << ", Model: " << model << std::endl;}
};int main() {std::string alice = "Alice";std::string bob = "Bob";// Initializing objects of class Car using initialization listCar car1(alice, "Toyota");car1.displayInfo();Car car2(bob, "Honda");car2.displayInfo();return 0;
}
<< Owner: Alice, Model: Toyota
<< Owner: Bob, Model: Honda

析构函数

类的析构函数是类的一种特殊的成员函数,它会在每次删除所创建的对象时执行。

析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号(~)作为前缀,它不会返回任何值,也不能带有任何参数。析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。

子类会继承父类的析构函数,但并不是通过继承其实现。当子类的对象销毁时,会先调用子类的析构函数,然后再调用父类的析构函数。这种顺序确保了对象的析构顺序与构造顺序相反。
如果父类有虚析构函数(通过在析构函数前面加上 virtual关键字),那么子类也会继承这个虚析构函数。这是为了实现多态性,确保在使用基类指针指向派生类对象时正确地释放资源。

加上 virtual 关键字确实是声明虚函数的方式,但在析构函数前面加上 virtual关键字有特定的作用。这种做法是为了确保正确的析构函数调用顺序和资源释放。

当你有一个基类指针指向派生类对象时,如果基类的析构函数是虚函数(即声明为virtual),那么在销毁这个对象时,会根据实际对象的类型(基类或派生类)来调用适当的析构函数。这种机制称为动态绑定或者多态性。

具体来说,如果基类的析构函数不声明为虚函数,那么当你通过基类指针删除一个派生类对象时,只会调用基类的析构函数,而不会调用派生类的析构函数。这可能导致派生类对象中的资源(如动态分配的内存)没有被正确释放,从而产生内存泄漏或其他问题。

因此,为了确保在继承体系中正确地释放资源,通常建议在基类的析构函数前面加上 virtual关键字,以启用动态绑定机制。这样,无论通过基类指针如何删除对象,都能正确调用对象的实际类型的析构函数,确保资源的正确释放。

总结一下,加上 virtual 关键字在析构函数中的作用是确保在继承体系中正确地调用对象的析构函数,从而实现多态性和正确的资源管理。

#include <iostream>using namespace std;class Line
{public:void setLength( double len );double getLength( void );Line();   // 这是构造函数声明~Line();  // 这是析构函数声明private:double length;
};// 成员函数定义,包括构造函数
Line::Line(void)
{cout << "Object is being created" << endl;
}
Line::~Line(void)
{cout << "Object is being deleted" << endl;
}void Line::setLength( double len )
{length = len;
}double Line::getLength( void )
{return length;
}
// 程序的主函数
int main( )
{Line line;// 设置长度line.setLength(6.0); cout << "Length of line : " << line.getLength() <<endl;return 0;
}
<< Object is being created
<< Length of line : 6
<< Object is being deleted

注意

初始化顺序最好要按照变量在类声明的顺序一致,否则会出现下面的特殊情况:

#include<iostream>using namespace std;class Student1 {public:int a;int b;void fprint(){cout<<" a = "<<a<<" "<<"b = "<<b<<endl;}Student1(int i):b(i),a(b){ }    //异常顺序:发现a的值为0  b的值为2  说明初始化仅仅对b有效果,对a没有起到初始化作用 
//         Student1(int i):a(i),b(a){ } //正常顺序:发现a = b = 2 说明两个变量都是初始化了的  Student1()                         // 无参构造函数{ cout << "默认构造函数Student1" << endl ;}Student1(const Student1& t1) // 拷贝构造函数{cout << "拷贝构造函数Student1" << endl ;this->a = t1.a ;}Student1& operator = (const Student1& t1) // 赋值运算符{cout << "赋值函数Student1" << endl ;this->a = t1.a ;return *this;}};
class Student2
{public:Student1 test ;Student2(Student1 &t1){test  = t1 ;}
//     Student2(Student1 &t1):test(t1){}
};
int main()
{Student1 A(2);        //进入默认构造函数 Student2 B(A);        //进入拷贝构造函数 A.fprint();            //输出前面初始化的结果 
}

在这里插入图片描述
一个类内可以有多个构造函数,可以是一般类型的,也可以是带参数的,相当于重载构造函数,但是析构函数只能有一个

class Matrix
{
public:Matrix(int row, int col);                                            //普通构造函数Matrix(const Matrix& matrix);                                        //拷贝构造函数Matrix();                                                            //构造空矩阵的构造函数void print(void);~Matrix();
};

总结上述两点注意事项,代码如下:

#include<iostream>using namespace std;class Student1 {
public:int a=0;int b=0;void fprint() {cout << " a = " << a << " " << "b = " << b << "\n"<<endl;}Student1(){cout << "无参构造函数Student1" << endl;}Student1(int i):a(i),b(a){ cout << "有参参构造函数Student1" << endl;} Student1(const Student1& t1){cout << "拷贝构造函数Student1" << endl;this->a = t1.a;this->b = t1.b;}Student1& operator = (const Student1& t1) // 重载赋值运算符{cout << "赋值函数Student1" << endl;this->a = t1.a;this->b = t1.b;return *this;}};
class Student2
{
public:Student1 test;Student2(Student1& t1) {t1.fprint();cout << "D: ";test = t1;}//     Student2(Student1 &t1):test(t1){}
};
int main()
{cout << "A: ";Student1 A;A.fprint();cout << "B: ";Student1 B(2); B.fprint();cout << "C: ";Student1 C(B); C.fprint(); cout << "D: ";Student2 D(C);D.test.fprint();
}/*A: 无参构造函数Student1a = 0 b = 0B: 有参参构造函数Student1a = 2 b = 2C:拷贝构造函数Student1a = 2 b = 2D: 无参构造函数Student1a = 2 b = 2D: 赋值函数Student1a = 2 b = 2

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

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

相关文章

Pytorch使用教学4-张量的索引

1 张量的符号索引 张量也是有序序列&#xff0c;我们可以根据每个元素在系统内的顺序位置&#xff0c;来找出特定的元素&#xff0c;也就是索引。 1.1 一维张量的索引 一维张量由零维张量构成 一维张量索引与Python中的索引一样是是从左到右&#xff0c;从0开始的&#xff…

搭建NFS、web、dns服务器

目录 1、搭建一个nfs服务器&#xff0c;客户端可以从该服务器的/share目录上传并下载文件 服务端配置&#xff1a; 客户端测试&#xff1a; 2、搭建一个Web服务器&#xff0c;客户端通过www.haha.com访问该网站时能够看到内容:this is haha 服务端配置&#xff1a; 客户端…

【Web爬虫逆向】“企业预警通”模糊查询公司信息,逆向案例实战

“企业预警通”模糊查询公司信息&#xff0c;逆向案例实战 功能介绍效果演示思路分析1、先找到模糊查询的接口2、分析headers与params中参数并进行构造3、JS逆向&#xff0c;跟栈&#xff0c;找到js中key和dataCategory的生成方法&#xff0c;并完成js补码构造4、成功还原key后…

UART编程框架详解

1. UART介绍 UART&#xff1a;通用异步收发传输器&#xff08;Universal Asynchronous Receiver/Transmitter)&#xff0c;简称串口。 调试&#xff1a;移植u-boot、内核时&#xff0c;主要使用串口查看打印信息 外接各种模块 1.1 硬件知识_UART硬件介绍 UART的全称是Unive…

新160个crackme - 011-wocy.1

运行分析 显示Unregister&#xff0c;点击注册无反应&#xff0c;猜测要先注册 PE分析 C 程序&#xff0c;32位&#xff0c;无壳 静态分析 ida发现关键字符串&#xff0c;进入关键函数 动态调试 设置断点动态调试&#xff0c;CWnd::UpdateData(true) &#xff1a;用于将屏幕上控…

MATLAB基础:数据和变量

今天我们开始学习MATLAB基础知识 1、常用非运算符及其作用 1、“,” 作为程序运行的分隔符&#xff0c;起到分隔语句的作用 2、“;” 同样作为分隔符&#xff0c;与“,”不同的是“;”会在程序运行时隐藏该行语句 如下图&#xff1a; 3、“...” 三个英文句点表示续行符…

W30-python03-pytest+selenium+allure访问百度网站实例

此篇文章为总结性&#xff0c;将pystest、selenium、allure结合起来 功能如下&#xff0c;web自动化&#xff0c;输入baidu网站&#xff0c;搜索“雷军”、打开网页中第一条内容 pytestsel.py如下&#xff1a; import time import re import allure import pytest from tools…

提升ROI:利用高级爬虫技术优化营销策略

如何通过高级爬虫技术高效提升营销ROI&#xff1f; 摘要&#xff1a; 在当今数据驱动的营销环境中&#xff0c;提升投资回报率&#xff08;ROI&#xff09;的关键在于精准洞察市场与用户行为。本文将探讨如何运用高级爬虫技术来优化营销策略&#xff0c;从海量互联网数据中挖掘…

【数据分享】2008-2022年我国省市县三级的逐日NO2数据(excel\shp格式)

空气质量数据是在我们日常研究中经常使用的数据&#xff01;之前我们给大家分享了2000-2022年的省市县三级的逐日PM2.5数据、2013-2022年的省市县三级的逐日CO数据和2013-2022年的省市县三级的逐日SO2数据&#xff08;均可查看之前的文章获悉详情&#xff09;&#xff01; 本次…

jmeter实战(1)- Mac环境安装

一、安装 JDK 这个就不介绍了&#xff0c;本地自行安装 JDK 并且配置好环境变量 二、安装 Jmeter 1. 下载地址 —> 下载链接点击这里 2. 选择合适的版本下载 3. 解压到本地目录 解压后&#xff0c;会得到下面的目录文件&#xff1a; 输入cd bin&#xff0c;进入到bin…

[STM32]HAL库实现自己的BootLoader-BootLoader与OTA-STM32CUBEMX

目录 一、前言 二、BootLoader 三、BootLoader的实现 四、APP程序 五、效果展示 六、拓展 一、前言 听到BootLoader大家一定很熟悉&#xff0c;在很多常见的系统中都会存在BootLoader。本文将介绍BootLoader的含义和简易实现&#xff0c;建议大家学习前掌握些原理基础。 …

【Android】Activity与Fragment的数据传递

上一篇文章学到了碎片的创建与生命周期&#xff0c;接下来学习碎片的常用操作&#xff0c;其中会用到上一篇文章的三个碎片&#xff0c;就做一个简单的说明吧&#xff1a;LeftFragment&#xff08;包含一个按钮&#xff09;、RightFragment4&#xff08;以粉色为背景的文本&…

408专业课130|零基础五个月速成攻略

计算机考研&#xff0c;有两个选择&#xff0c;一个是自命题&#xff0c;一个是408。如果你只是考一个普通院校&#xff0c;可以选择考自命题院校&#xff0c;容易上岸&#xff0c;但是如果考985/211/这类院校&#xff0c;最好还是选择408&#xff0c;因为408的考风险能力很强&…

Apollo部署与简易架构梳理

文章目录 apollo 安装apollo的基本架构组件机制component编译与加载 节点通讯数据的传输消息读写的实现消息的写端消息读端 常用术语ComponentChannelTaskNodeReader/WriterService/ClientParameter服务发现CRoutineSchedulerMessageDag文件Launch文件Record文件Mainboard Moni…

在图神经网络(GNN)上进行关系推理的新架构

开发能够学习推理的模型是一个众所周知的具有挑战性的问题&#xff0c;在这个领域中&#xff0c;使用图神经网络&#xff08;GNNs&#xff09;似乎是一个自然的选择。然而&#xff0c;以往关于使用GNNs进行推理的工作表明&#xff0c;当这些模型面对需要比训练时更长推理链的测…

(leetcode学习)236. 二叉树的最近公共祖先

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为&#xff1a;“对于有根树 T 的两个节点 p、q&#xff0c;最近公共祖先表示为一个节点 x&#xff0c;满足 x 是 p、q 的祖先且 x 的深度尽可能大&#xff08;一个节点也可以是它自己的祖…

【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第三篇 嵌入式Linux驱动开发篇-第五十一章 添加设备树节点

i.MX8MM处理器采用了先进的14LPCFinFET工艺&#xff0c;提供更快的速度和更高的电源效率;四核Cortex-A53&#xff0c;单核Cortex-M4&#xff0c;多达五个内核 &#xff0c;主频高达1.8GHz&#xff0c;2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT…

linux禁用root

linux禁用root 1. 禁止普通用户切换到root1.1 sudo -i和sudo -s的区别1.2 sudo -i和直接登录root账号的区别1.3 禁止sudo -i切换root1.4 禁止su - root切换root 2. 禁止root远程登录2.1 ssh禁止root登录2.2 禁止远程桌面登录 本文主要介绍&#xff1a; 如何禁止普通用户切换到r…

Java---后端事务管理

代码世界聚眸光&#xff0c;昼夜敲盘思绪长。 算法心间精构建&#xff0c;编程路上细思量。 屏前架构乾坤定&#xff0c;键上飞驰智慧扬。 默默耕耘成果现&#xff0c;创新科技铸辉煌。 目录 一&#xff0c;概念 二&#xff0c;Spring事务管理 三&#xff0c;rollbackFor事务回…

运维锅总浅析Kubernetes之Ceph

Ceph 的核心组件有哪些&#xff1f;Ceph读写数据流程及故障自愈是怎样的&#xff1f;如何对Ceph部署架构进行调优&#xff1f;如何用Ceph集成到kubernetes&#xff1f;希望本文能帮您解答这些疑惑&#xff01; 一、Ceph简介 Ceph 是一个开源的分布式存储系统&#xff0c;旨在…