当前位置: 首页 > news >正文

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

相关文章:

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

本文参考菜鸟教程&#xff0c;仅作笔记用。 构造函数 构造函数&#xff08;Constructor&#xff09;是一种特殊的方法&#xff0c;用于在创建对象时进行初始化操作。构造函数的名称与类的名称是完全相同的&#xff0c;并且不会返回任何类型&#xff0c;也不会返回 void。在面…...

Lc63---1859将句子排序(排序)---Java版(未写完)

1.题目描述 2.思路 &#xff08;1&#xff09;首先将句子按空格分割成若干单词。 &#xff08;2&#xff09;每个单词的最后一个字符是它的位置索引。我们可以通过这个索引将单词恢复到正确的位置。 &#xff08;3&#xff09;按照单词的索引顺序排序这些单词。 &#xff08;4…...

centos7-8/redhat7-8一键安装配置vsftp服务

1.脚本介绍 1.1.介绍&#xff1a; linux下一键安装及配置vsftpd服务 &#xff0c;通过执行install.sh脚本&#xff0c;脚本会根据参数区域的值执行安装和配置vsftp服务&#xff0c;安装后会创建一个默认ftp用户wangxf密码wangxf2023 1、支持自定义安装(更改脚本内参数值) 2、…...

7月24日JavaSE学习笔记

序列化版本控制 序列化&#xff1a;将内存对象转换成序列&#xff08;流&#xff09;的过程 反序列化&#xff1a;将对象序列读入程序&#xff0c;转换成对象的方式&#xff1b;反序列化的对象是一个新的对象。 serialVersionUID 是一个类的序列化版本号 private static fin…...

微信小程序教程008:事件绑定

文章目录 事件绑定1、什么是事件2、小程序中常用事件3、事件对象的属性列表4、target和currentTarget的区别5、bindtap的语法格式6、在事件处理函数中为data中的数据赋值7、事件传参数8、bind:input语法格式9、实现文本框和data之间的数据同步事件绑定 1、什么是事件 事件是渲…...

企业级-实现Nginx的静态文件服务器映射

作者&#xff1a;fyupeng 技术专栏&#xff1a;☞ https://github.com/fyupeng 项目地址&#xff1a;☞ https://github.com/fyupeng/distributed-blog-system-api 留给读者 开发人员往往会经常需要通过浏览器下载文件、图片或者PDF或者缩略图等&#xff0c;这时候我们可以根据…...

CTF Web SQL注入 10000字详解

这里写目录标题 涉及的数据库知识unionorder bydatabase()information_schemalimit--空格注释replaceinto outfilelikeGROUP BYHAVINGGROUP BY、HAVING、WHERE之间的关系regexp 原理信息收集操作系统数据库判断注入点注入点类型POST注入数字型注入字符型注入搜索型注入Insert/u…...

动态SLAM:如何判断一个特征是动态特征(对极几何)

文章目录 1.什么是极线、极点和极面2.如何判断其为动态点特征3.如何判断其为动态线特征 1.什么是极线、极点和极面 由图可知&#xff0c;C1,C2,X(X1,X2)组成了一个三角平面&#xff0c;这个三角所在的平面就是极面 在这个极平面中&#xff0c;和成像平面相交的线是极线&#xf…...

【C++】初识C++基础篇·一(命名空间,函数重载,缺省参数,引用);

文章目录 前言1.输入与输出输出输入cin和scanf的对比 2.命名空间2.1namespace存在的意义2.2namespace的使用3.缺省参数4.函数重载重载函数的调用规则 5.引用 前言 我们先通过一段简单的代码来拉开C的序幕&#xff1b; //text.cpp #include<iostream> #include<stdio…...

2024年道路运输安全员考试题库及答案

一、判断题 1.国家制订《中华人民共和国道路交通安全法》的目的是为了维护道路交通秩序&#xff0c;预防和减少交通事故&#xff0c;保护人身安全&#xff0c;保护公民、法人和其他组织的财产安全及其他合法权益&#xff0c;提高通行效率。 答案&#xff1a;正确 2.依据《中…...

Linux Vim教程(十二):语法高亮与代码折叠

目录 1. 语法高亮 1.1 启用语法高亮 1.2 设置配色方案 1.3 自定义语法高亮 2. 代码折叠 2.1 启用代码折叠 2.2 设置折叠方法 2.3 手动折叠 2.4 基于缩进的折叠 2.5 基于语法的折叠 3. 案例 3.1 配置文件 3.2 编辑Python文件 3.3 使用折叠功能 4. 高级使用技巧 …...

JavaScript(18)——事件类型,事件对象

事件类型 鼠标事件&#xff1a; click&#xff1a;鼠标点击 mouseenter&#xff1a;鼠标经过 mouseleave&#xff1a;鼠标离开 焦点事件&#xff1a; focus&#xff1a;获得焦点 blur&#xff1a;失去焦点 键盘事件&#xff1a; Keydown&#xff1a;键盘按下触发 Keyup&#…...

有效组织离散变量:指针数组在C语言中的应用

把离散变量组织起来&#xff0c;访问起来更加方便&#xff0c;无需一个个变量单独赋值。 如modbus读写reg&#xff0c; 把a\b\c\d实时变化分散的变量组织成一个数组reg&#xff0c;方便获取 相当于变量的内存地址池 int main() {int a 10, b 20, c 30, d 40;int i;int *re…...

qt 应用正在运行时,如何更新升级exe文件

在Qt应用正在运行时&#xff0c;直接替换同名的.exe文件可能会导致文件正在使用中的错误。为了安全地更新.exe文件&#xff0c;你可以采取以下步骤&#xff1a; 创建一个临时的.exe文件。 等待当前.exe文件的进程关闭。 删除原有的.exe文件。 将临时.exe文件重命名为原有的.…...

git的rebase 和 merge 的区别

rebase 和 merge 的区别 Merge&#xff08;合并&#xff09;和 Rebase&#xff08;变基&#xff09;是 Git 中两种常用的分支整合方式&#xff0c;它们有不同的工作原理和适用场景&#xff1a; Merge&#xff08;合并&#xff09;&#xff1a; ● Merge 操作将两个分支的不同提…...

django基于大数据的电影推荐系统-计算机毕业设计源码71246

目 录 摘 要 1 绪论 1.1 选题背景与意义 1.2研究现状 1.3研究内容 1.4 开发环境 1.5论文结构与章节安排 2 相关理论和技术 2.1 协同过滤算法 2.2 B/S体系结构介绍 2.3 Python爬虫技术 2.4 Django框架介绍 2.5 MySQL数据库 3 电影推荐系统系统分析 3.1 可行性分析…...

reverse_re3-入土为安的第十天

一个迷宫题 shirtf12发现flag字样 点进去发现迷宫 675 应该是3 * 15*15 即有三个迷宫 shifte提取 import numpy as np# 你的数据&#xff08;长度应为 225 的倍数&#xff09; dword_202020 [1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,3,1,1,0,0,0,0,0,0,1,1,1,1,1,0,0,…...

fastapi之一

文章目录 安装运行HTTP 请求方法示例 POSTMAN 检验GETPOSTPUTDELETE HTTP 状态码示例GETPOST 综合示例POSTGETPUTDELETE 总结 安装 pip install fastapi或者 pip install fastapi -i https://mirrors.aliyun.com/pypi/simple上面两种方法均可以&#xff0c;选择一个即可&…...

【C语言报错已解决】Format String Vulnerability

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 引言 在日常开发中&#xff0c;我们经常会遇到各种各样的bug&#xff0c;其中格式化字符串漏洞报错可能是最让人头疼的一种。这…...

关于一个简单的顺序表代码

1.首先是头文件SeqList.h的代码&#xff1a; #pragma once #include<stdio.h> #include<assert.h> #include<stdlib.h> typedef int SXBint; typedef struct SL {SXBint* a;int size;int capacity; }SLnode; //初始化 void SeqLsitInit(SLnode* ps); //尾插…...

.Net框架,除了EF还有很多很多......

文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...

遍历 Map 类型集合的方法汇总

1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...

条件运算符

C中的三目运算符&#xff08;也称条件运算符&#xff0c;英文&#xff1a;ternary operator&#xff09;是一种简洁的条件选择语句&#xff0c;语法如下&#xff1a; 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true&#xff0c;则整个表达式的结果为“表达式1”…...

Spring AI与Spring Modulith核心技术解析

Spring AI核心架构解析 Spring AI&#xff08;https://spring.io/projects/spring-ai&#xff09;作为Spring生态中的AI集成框架&#xff0c;其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似&#xff0c;但特别为多语…...

HashMap中的put方法执行流程(流程图)

1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中&#xff0c;其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下&#xff1a; 初始判断与哈希计算&#xff1a; 首先&#xff0c;putVal 方法会检查当前的 table&#xff08;也就…...

Xen Server服务器释放磁盘空间

disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...

Fabric V2.5 通用溯源系统——增加图片上传与下载功能

fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...

AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别

【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而&#xff0c;传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案&#xff0c;能够实现大范围覆盖并远程采集数据。尽管具备这些优势&#xf…...

从“安全密码”到测试体系:Gitee Test 赋能关键领域软件质量保障

关键领域软件测试的"安全密码"&#xff1a;Gitee Test如何破解行业痛点 在数字化浪潮席卷全球的今天&#xff0c;软件系统已成为国家关键领域的"神经中枢"。从国防军工到能源电力&#xff0c;从金融交易到交通管控&#xff0c;这些关乎国计民生的关键领域…...

Java求职者面试指南:Spring、Spring Boot、Spring MVC与MyBatis技术解析

Java求职者面试指南&#xff1a;Spring、Spring Boot、Spring MVC与MyBatis技术解析 一、第一轮基础概念问题 1. Spring框架的核心容器是什么&#xff1f;它的作用是什么&#xff1f; Spring框架的核心容器是IoC&#xff08;控制反转&#xff09;容器。它的主要作用是管理对…...