C++ 类和对象 构造 / 析构函数

news2024/12/23 15:53:22

一 类的6个默认成员函数:

如果一个类中什么成员都没有,简称为空类。

例:

#include <iostream>
class Empty 
{
    // 空类,什么成员都没有
};

空类中真的什么都没有吗?并不是,任何类在什么都不写时,编译器会自动生成以下6个默认成员 函数。

默认构造函数:如果用户没有定义任何构造函数,编译器会自动生成一个默认构造函数。

拷贝构造函数:用于创建一个对象是另一个对象的副本。如果用户没有定义,编译器会生成一个默认的拷贝构造函数。

拷贝赋值运算符:用于将一个对象赋值给另一个对象。如果用户没有定义,编译器会生成一个默认的拷贝赋值运算符。

移动构造函数:用于将资源从一个对象移动到另一个对象。如果用户没有定义,编译器会生成一个默认的移动构造函数。

移动赋值运算符:用于将资源从一个对象移动并赋值给另一个对象。如果用户没有定义,编译器会生成一个默认的移动赋值运算符。

析构函数:用于销毁对象并释放资源。如果用户没有定义,编译器会生成一个默认的析构函数。

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

二 构造函数:

2.1:构造函数的概念:

构造函数是一个特殊的成员函数,它的名称与类名相同,没有返回值。在创建类的对象时,构造函数由编译器自动调用,用于初始化对象的数据成员

2.2:构造函数的特征

函数名与类名相同。

没有返回值。

在对象实例化时由编译器自动调用。

构造函数可以重载,即一个类可以有多个构造函数,只要它们的参数列表不同。

2.3:无参/有参构造函数代码示例

class Date
{
public:
    //有参数的构造函数:
    //Date(int _year = 1999 , int _month = 2 , int _day = 26)
    
    // 无参构造函数:
    Date() //函数名与类名相同。
    
    {
        // 使用 this 指针访问成员变量
        this->_year = 2024;
        this->_month = 7;
        this->_day = 6;
    }

    void Print()
    {
        std::cout << this->_year << "-" << this->_month << "-" << this->_day << std::endl;
    }

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

int main()
{
    Date d1; //调用无参数构造函数 
    d1.Print();

    //Date d2(2022, 7, 6); //调用带参构造函数
    //d2.Print();
    return 0;
}

Date()是无参的构造函数,没有参数。在对象 d1 创建时自动调用。你们有可能会问了,为什么在无参的构造函数里面this指针指向成员变量,那为什么main函数里见不到this指针呢?因为当 Date d1; 创建对象时,编译器会自动传递 d1 的地址给 this 指针所以就不需要显示this指针。

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

2.4:默认构造函数代码示例

class Date 
{
public:
    void Print() 
    {
        std::cout << _year << "-" << _month << "-" << _day << std::endl;
    }

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

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

输出:

上面代码因我未显示定义构造函数所以编译器帮我生成了一个默认的构造函数而且是看不见的,那为什么默认生成的输出的值是随机值呢?

原来C++把类型分为内置类型和自定义类型,内置类型就是语言提供的基本数据类型,如intchar等。自定义类型是用户定义的类型,如使用classstructunion定义的类型。

2.5:内置类型和自定义类型的默认构造函数处理

内置类型:

   1.内置类型的成员变量在默认构造函数中不会被自动初始化

   2.如果不显式初始化,成员变量的值将是未定义的(即随机值)

自定义类型:

   1.自定义类型的成员变量在默认构造函数中会调用其默认构造函数。

   这意味着,即使你没有显式定义自定义类型的构造函数,编译器也会自动调用默认构造函数来       初始化这些成员变量。

例子:

class Time 
{
public:
    Time() 
    {
         // Time类的无参构造函数
        std::cout << "Time()" << std::endl;
        _hour = 0;
        _minute = 0;
        _second = 0;
    }

private:
    int _hour;
    int _minute;
    int _second;
};

class Date 
{
public:
    Date()// 初始化内置类型成员变量
    {
        this->_year = 2024;
        this->_month = 7;
        this->_day = 5;
    }

    void Print()
    {
        std::cout << _year << "-" << _month << "-" << _day << std::endl;
    }

private:
    int _year;  // 内置类型
    int _month; // 内置类型
    int _day;   // 内置类型
    Time _t;    // 自定义类型
};

int main() 
{
    Date d;  // 调用无参构造函数
    d.Print();
    return 0;
}

输出:

我们来说一下它的执行顺序:首先是执行主函数main当执行到 Date d; 时编译器就会先去调用自定义函数Time_t;然后等它全部初始化完成 再去调用无参数构造并且初始化里面的内置类型。

那我们这是显式定义自定义类型的构造函数并且给成员变量赋值了,所以就不会出现随机值,如果想要显式定义自定义类型的构造函数并且不想要随机值那该怎么办呢?这时候C++11 中针对内置类型成员不初始化的缺陷,又打了补丁,即:内置类型成员变量在类中声明时可以给默认值。

例子:

class Date 
{
public:
    void Print()
    {
        std::cout << _year << "-" << _month << "-" << _day << std::endl;
    }

private:
    int _year = 2024;  // 内置类型
    int _month = 2; // 内置类型
    int _day = 1;   // 内置类型
};

输出:

2.6:默认构造函数

在C++中,默认构造函数是指在创建对象时不需要提供任何参数的构造函数。默认构造函数可以分为两种:

  1. 默认构造函数:一个类只能有一个真正的默认构造函数(不需要参数)。
  2. 无参构造函数和全缺省参数构造函数
    • 如果参数不同,它们会重载,编译器不会报错。
    • 如果参数相同(即都没有参数),它们就相当于有两个默认构造函数,这时编译器会报错,因为无法区分调用哪个构造函数。

关键点:

无参构造函数:没有参数的构造函数。 全缺省参数构造函数:所有参数都有默认值的构造函数。 重载:当构造函数的参数列表不同,它们可以共存且不会冲突。

例子:

class Date 
{
public:
    // 无参构造函数
    Date() 
    {
        _year = 2024;
        _month = 7;
        _day = 2;
    }

    // 全缺省参数构造函数
    Date(int year = 2023, int month = 1, int day = 1) 
    {
        _year = year;
        _month = month;
        _day = day;
    }

    void Print()
    {
        std::cout << _year << "-" << _month << "-" << _day << std::endl;
    }

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

int main() 
{
    Date d;  // 调用无参构造函数
    d.Print();

    Date d2(2023, 4, 3);  // 调用全缺省参数构造函数
    d2.Print(); 
    return 0;
}

输出:

之所以会报错是因为全缺省参数构造函数和无参数构造函数它们都有自己的默认值,当执行到Date d; 时它并不知道到底要调用哪一个所以就会报错,那怎么更改呢?只需要把全缺省参数的默认值给去掉就行了,这样编译器就不会迷糊到底要调用哪一个了

三 析构函数:

3.1 析构函数的概念:

通过前面构造函数的学习,我们知道一个对象是怎么来的,那一个对象又是怎么没呢的? 析构函数:与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由 编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。

3.2 析构函数的特征:

1:名称:析构函数的名称是在类名之前加上~

2:无参数无返回值:析构函数没有参数,也没有返回值。

3:唯一性:每个类只能有一个析构函数。

4:自动调用:当对象的生命周期结束时,析构函数会被编译器自动调用。

5:不可重载:析构函数不能像其他成员函数一样被重载。

3.3:代码示例:


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

    ~Time() 
    {
        std::cout << "~Time()" << std::endl;
    }

private:
    int _hour;
    int _minute;
    int _second;
};

class Date 
{
public:
    Date() 
    {
        std::cout << "Date()" << std::endl;
        _year = 2024;
        _month = 7;
        _day = 2;
    }

    Date(int year, int month, int day) 
    {
        _year = year;
        _month = month;
        _day = day;
    }

    ~Date() 
    {
        std::cout << "~Date()" << std::endl;
    }

    void Print()
    {
        std::cout << _year << "-" << _month << "-" << _day << std::endl;
    }

private:
    int _year;
    int _month;
    int _day;
    Time _t;  // 自定义类型成员变量
};

int main() 
{
    Date d1;  // 创建Date对象,调用Date构造函数和Time构造函数
    d1.Print();

    Date d2(2023, 4, 3);
    d2.Print();

    return 0;
}

输出:

我们现在来捋一下它的执行过程:

首先从主函数进入之后就会执行无参的构造函数但因无参构造函数里有自定义类型成员变量所以先要调用Time(); 你们是不是调用完它之后直接就跳动析构函数,这是不被允许的 原因是析构函数要等对象的生命周期结束时,析构函数会被编译器自动调用,自定义类型成员变量执行完了那就该执行无参构造函数了,然后就这样循环直到程序结束的时候就会调用d1和d2的析构函数,先调用Date的析构函数,再调用Time的析构函数。

根据上面析构输出的打印那咱们想没想过为什么是先调用Date然后再调用Time呢?跟上面的无参数构造函数的输出截然不同这是什么原因呢?

3.4 构造函数和析构函数的调用顺序:

构造函数调用顺序:

1. 首先调用成员变量的构造函数

2. 然后调用包含这些成员变量的类的构造函数。

析构函数调用:

1. 首先调用包含这些成员变量的类的析构函数。

2. 然后调用成员变量的析构函数。

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

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

相关文章

使用预加载库优化 PostgreSQL 函数#postgresql认证

在 POSTGRESQL 中执行函数和过程 为了理解 PostgreSQL 的工作原理&#xff0c;我们首先要看一个简单的函数调用。下一个清单显示了一些简单的PostGIS代码&#xff1a; PgSQL test# timing Timing is on. test# SELECT * FROM hans.points WHERE id 1;id │ …

南大通用数据库-Gbase-8a-学习-44-DDLEVENT恢复

目录 一、环境信息 二、前景提要 1、情况描述 2、3号节点gc_recover日志截图 3、3号节点express日志截图 4、ddlevent截图 5、报错赋权语句分别在1节点和4节点执行 6、gcadmin 三、解决方法 1、描述 2、清理系统user表DDLEVENT 3、拷贝系统user表数据 &#xff08;…

【游戏客户端】大话slg玩法架构(一)滚动基类

【游戏客户端】大话slg玩法架构&#xff08;一&#xff09;滚动基类 大家好&#xff0c;我是Lampard家杰~~ 今天我们兑现诺言&#xff0c;给大家分享SLG玩法的实现j架构&#xff0c;关于SLG玩法的介绍可以参考这篇上一篇文章&#xff1a;【游戏客户端】制作率土之滨Like玩法 PS…

React -- useState状态更新异步特性——导致获取值为旧值的问题

useState状态异步更新 问题导致的原因解决办法进一步分析后续遇到的新问题 问题 const [isSelecting, setIsSelecting] useState(false);useEffect(() > {const handleKeyDown (event) > {if (event.key Escape) {if(isSelectingRef){//.......setIsSelecting(!isSele…

项目机会:4万平:智能仓,AGV,穿梭车,AMR,WMS,提升机,机器人……

导语 大家好&#xff0c;我是社长&#xff0c;老K。专注分享智能制造和智能仓储物流等内容。 如下为近期国内智能仓储物流相关项目的公开信息线索&#xff0c;这些项目具体信息会发布到知识星球&#xff0c;请感兴趣的球友先人一步到知识星球【智能仓储物流技术研习社】自行下载…

LeetCode热题100刷题9:25. K 个一组翻转链表、101. 对称二叉树、543. 二叉树的直径、102. 二叉树的层序遍历

25. K 个一组翻转链表 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int x) : val(x), next(nullptr) {}* ListNode(int x, ListNode *next) : val(x), nex…

Java | Leetcode Java题解之第223题矩形面积

题目&#xff1a; 题解&#xff1a; class Solution {public int computeArea(int ax1, int ay1, int ax2, int ay2, int bx1, int by1, int bx2, int by2) {int area1 (ax2 - ax1) * (ay2 - ay1), area2 (bx2 - bx1) * (by2 - by1);int overlapWidth Math.min(ax2, bx2) -…

代发考生战报:南京考场华为售前HCSP H19-411考试通过

代发考生战报&#xff1a;南京考场华为售前HCSP H19-411考试通过&#xff0c;客服给的题库非常稳定&#xff0c;考试遇到2个新题&#xff0c;剩下全是题库里的原题&#xff0c;想考的放心考吧&#xff0c;考场服务挺好&#xff0c;管理员带着做签名和一些考试说明介绍清楚&…

C++相关概念和易错语法(16)(list)

1.list易错点 &#xff08;1&#xff09;慎用list的sort&#xff0c;list的排序比vector慢得多&#xff0c;尽管两者时间复杂度一样&#xff0c;甚至不如先把list转为vector&#xff0c;用vector排完序后再转为list &#xff08;2&#xff09;splice是剪切链表&#xff0c;将…

AGAST (角点检测)

AGAST检测原理 AGAST(Adaptive and Generic Accelerated Segment Test)算法是Elmar于2010年提出的特征检测算法,改进了FAST(Features from Accelerated Segment Test)特征检测方法,使其具有更快的速度和更好的鲁棒性。AGAST算法提供了比FAST算法更详细的特征标记方式和判断依…

读人工智能全传07智能体

1. 布鲁克斯革命 1.1. 随着科学认知的发展&#xff0c;有时候旧有科学体系会面临全盘崩溃的危机&#xff0c;新的科学体系诞生&#xff0c;取代传统的、既定的科学体系&#xff0c;这就意味着科学的范式将发生变化 1.2. 澳大利亚机器人学家罗德尼布鲁克斯(Rodney Brooks)&…

vue3+ el-tree 展开和折叠,默认展开第一项

默认第一项展开: 展开所有项&#xff1a; 折叠所有项&#xff1a; <template><el-treestyle"max-width: 600px":data"treeData"node-key"id":default-expanded-keys"defaultExpandedKey":props"defaultProps"…

Qt creator 控件转到槽 报错 The class containing “Ui:Dialog“ could not be found in

今天调试程序&#xff0c;发现主界面控件转到槽&#xff0c;报错如下图&#xff1a; 问题表现为&#xff1a;只有主窗口控件有这个错误&#xff0c;其他子窗口正常。 解决&#xff1a; 在网上搜这个报错信息&#xff0c;都没有一个很好的解决办法。 最后发现是我在子窗口中要…

004-基于Sklearn的机器学习入门:回归分析(下)

本节及后续章节将介绍机器学习中的几种经典回归算法&#xff0c;包括线性回归&#xff0c;多项式回归&#xff0c;以及正则项的岭回归等&#xff0c;所选方法都在Sklearn库中聚类模块有具体实现。本节为下篇&#xff0c;将介绍多项式回归和岭回归等。 2.3 多项式回归 在一般的…

Visual Studio Code 教程 VsCode安装Live Server以服务形式打开html

搜索Live Server 插件,然后安装 选一个html文件&#xff0c;右键点击 Open with live server,然后就自动弹出来了

怎样优化 PostgreSQL 中对日期时间范围的模糊查询?

文章目录 一、问题分析&#xff08;一&#xff09;索引未有效利用&#xff08;二&#xff09;日期时间格式不统一&#xff08;三&#xff09;复杂的查询条件 二、优化策略&#xff08;一&#xff09;使用合适的索引&#xff08;二&#xff09;规范日期时间格式&#xff08;三&a…

北森锐途人才竞聘盘点管理测评:高管领导力六大评判标准深度解析万达商管中国绿发等

北森锐途人才管理测评&#xff1a;高管领导力评判标准深度解析 在企业高管的盘点与竞聘测评领域&#xff0c;众多管理人才面临评估自身领导力的挑战。面对能力卓越、职级显赫的同僚&#xff0c;许多管理者感到缺乏一套权威且专业的评价体系。然而&#xff0c;无论是天赋异禀的领…

Html5前端基本知识整理与回顾上篇

今天我们结合之前上传的知识资源来回顾学习的Html5前端知识&#xff0c;与大家共勉&#xff0c;一起学习。 目录 介绍 了解 注释 标签结构 排版标签 标题标签 ​编辑 段落标签 ​编辑 换⾏标签 ​编辑 ⽔平分割线 ⽂本格式化标签 媒体标签 绝对路径 相对路径 …

Chromium编译指南2024 Linux篇-安装官方工具depot_tools(二)

1.引言 在上一节中&#xff0c;我们已经完成了 Git 的安装&#xff0c;并了解了其在 Chromium 编译过程中的重要性。接下来&#xff0c;我们将继续进行环境的配置&#xff0c;首先是安装和配置 Chromium 编译所需的重要工具——depot_tools。 depot_tools 是一组用于获取、管…

怎样优化 PostgreSQL 中对布尔类型数据的查询?

文章目录 一、索引的合理使用1. 常规 B-tree 索引2. 部分索引 二、查询编写技巧1. 避免不必要的类型转换2. 逻辑表达式的优化 三、表结构设计1. 避免过度细分的布尔列2. 规范化与反规范化 四、数据分布与分区1. 数据分布的考虑2. 表分区 五、数据库参数调整1. 相关配置参数2. 定…