【C++】构造函数

前言

在C语言中,当我们定义了一个结构体时,通常需要编写一个函数来初始化它,否则在创建结构体变量时容易忘记调用初始化函数,导致程序出错。但在C++中,我们将不会有这样的烦恼,前提是编写了正确的构造函数。


构造函数

构造函数是类的特殊成员函数之一,在实例化对象时由编译器自动调用,且在对象整个生命周期内仅调用一次。尽管名为“构造”,但构造函数的主要任务并非是开空间创建对象,而是初始化对象。当对象被定义时,对象的整体空间被分配,但对象内的成员变量还未定义,构造函数的作用就是完成对象成员变量的初始化工作。需要注意的是,构造函数不能被显式调用,而是在对象创建时自动执行。


接下来以日期类为例,演示一下构造函数的一些书写规则:

class Date
{
public:// 构造函数Date(int year, int month, int day){_year = year;_month = month;_day = day;}// print 函数稍后用来测试,打印一下数据void print() { cout << _year << "-" << _month << "-" << _day << endl; }private:int _year; int _month;int _day;
};

函数名字与类名相同没有返回值(也不用写 void),且可以被重载。需要注意的是,编译器会在每个成员函数中隐藏一个指向对象本身的指针,即 `this` 指针,以便在函数内部访问对象的成员。


来看下它的使用:

int main()
{Date d1(2024, 1, 29);d1.print();return 0;
}

程序运行起来没有任何问题,但是很显然现在的构造函数是有缺陷的,在定义对象时必须给参数,因此我们需要重载一个无参的构造:

    // 在Date类中的,考虑到篇幅,就不每次都把整个类写出来Date(){_year = 1;_month = 1;_day = 1;}

但是这样还是有些多余,我们完全可以利用缺省参数,把有参无参合二为一: 

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

这样Date类的构造函数就比较完善了。


能否不写?

如果我们不写构造函数会发生什么?程序会崩溃吗?这里先给一个结论:当没有显示定义构造函数时,编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。那么问题来了,编译器生成的默认构造是什么行为呢?它会怎么去初始化我们的对象?先通过下面这段代码来探究一下:

class Date
{
public:void print() { cout << _year << "-" << _month << "-" << _day << endl; }private:int _year; int _month;int _day;
};int main()
{// 注意这里不传参数时不要带括号,不然成函数调用了!!!Date d1;d1.print();return 0;
}

现在我们的Date类完全是在裸奔,我们定义一个对象,并调用它的print函数,发现得到的全是随机值。可见编译器生成的默认构造对内置类型不处理值是不确定的。那对于自定义类型呢?继续通过下面这一段代码来探究一下:

class Stack
{
public:Stack(int capacity = 4){cout << "Stack(int capacity = 4)" << endl;}private:int* _a;int _size;int _capacity;
};class MyQueue
{
public:private:Stack _s1;Stack _s2;
};int main()
{MyQueue q;return 0;
}

这里栈的构造函数我们让它简单打印一下信息,看它有没有被调用 。程序运行后我们发现打印了两次信息,可见栈的构造函数被调用了两次。可见编译器生成的默认构造,对于自定义类型会去调用它的默认构造
这里提出了一个概念——默认构造,稍微解释一下什么是默认构造:
1. 无参数的构造函数 2. 有参数但同时是全缺省的构造函数 3. 编译器生成的。这三种都能称为默认构造,但是很显然这三种在一个类里只会存在一个,简而言之,不需要传参就能调用的都可以称为默认构造
倘若这个栈没有默认构造(把缺省值去掉),那么不好意思,编译器报错。这种情况怎么办呢?这时候就需要初始化列表登场了。


初始化列表

构造函数通常可以分为两个部分:初始化列表和函数体。在构造函数体内,我们可以进行变量的赋值操作,严格来说这并不是初始化,而是赋初值。因为初始化只能进行一次,而函数体内的赋值操作可以多次执行。因此成员变量的定义(初始化)是在初始化列表中进行的,


你可能会好奇为什么需要初始化列表,在函数体内赋值不也好好的,但是有些成员变量在定义时必须初始化,如:引用、const 修饰的成员变量。还有就是像上面提的没有默认构造函数的自定义类型成员也可以在初始化列表中通过传参来调用构造,或者不想用它的默认构造也可以在初始化列表里传参构造。


通过这段代码来看看用法:

 

 

class Stack
{
public:// 语法规定这样写Stack(int capacity = 4):_capacity(capacity),_size(0),_a(nullptr){cout << "Stack(int capacity = 4)" << endl;}private:int* _a;int _size;int _capacity;
};class MyQueue
{
public:MyQueue(){}private:Stack _s1;Stack _s2;
};int main()
{MyQueue q;return 0;
}

对于MyQueue来说,它的构造函数虽然什么都没有,但是它还是会去调用自定义类型成员的默认构造,因为初始化列表才是成员变量定义的地方,而初始化列表中又没有给自定义类型成员显示定义。

class Stack
{
public:Stack(int capacity):_capacity(capacity),_size(0),_a(nullptr){cout << "Stack(int capacity)" << endl;}private:int* _a;int _size;int _capacity;
};class MyQueue
{
public:MyQueue():_s1(1),_s2(2){}private:Stack _s1;Stack _s2;
};int main()
{MyQueue q;return 0;
}

现在对于没有默认构造函数的自定义类型成员,只需要在初始化列表中传参数就行了。 


C++11中新增了一个特性,可以在成员变量的声明那里给缺省值这个缺省值实际上就是在构造函数的初始化列表中使用的初始值。如下所示:

 

 

class MyQueue
{
public:MyQueue(){}private:Stack _s1 = 1;Stack _s2 = 2;
};

值得一提的是,这里除了缺省值,还涉及到隐式类型转换。 


小题目

class A
{
public:A(int a):_a1(a),_a2(_a1){}void Print() { cout << _a1 << " " << _a2 << endl; }private:int _a2;int _a1;
};int main()
{A aa(1);aa.Print();return 0;
}

这个程序在vs下运行得到的是1和随机值,因为成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关


补充 

一般情况下,我们需要自己编写构造函数。然而,如果成员变量在声明时已经有了缺省值,或者这些成员变量是自定义类型且具有默认构造函数,则可以考虑不编写构造函数。 值得注意的是,构造函数可以设为私有,这样做的话,外部代码将无法直接实例化对象,只能通过特殊手段如定位 new 来创建对象。 

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

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

相关文章

整数流理论

目录 一&#xff0c;k流 二&#xff0c;整数流 三&#xff0c;四色问题 一&#xff0c;k流 Tutte在研究四色问题时&#xff0c;开创了整数流理论。 他研究的具体问题是&#xff0c;给定一个有向图和一个k阶交换群&#xff0c;能不能找到一个函数&#xff0c;把图的每个边映…

SpringBoot项目实现热部署的配置方法

SpringBoot项目实现热部署的配置方法 1、什么是热部署&#xff1f; 热部署&#xff0c;就是在应用正在运行的时候升级软件&#xff0c;却不需要重新启动应用。 2、什么是SpringBoot热部署&#xff1f; SpringBoot热部署就是在项目正在运行的时候修改代码, 却不需要重新启动…

PCB的通孔,盲孔,埋孔

通孔&#xff1a;双层板从顶层到底层的打通&#xff0c;这样电流就能够从顶层到底层 盲孔&#xff1a;因为看不到底&#xff0c;像一口井一样&#xff0c;只能打到中间&#xff0c;里面灌上铜&#xff0c;我们可以从第一层切换到第二层&#xff0c;第三层等等&#xff0c;盲孔…

在Windows上安装与配置Apache服务并结合内网穿透工具实现公网远程访问本地内网服务

文章目录 前言1.Apache服务安装配置1.1 进入官网下载安装包1.2 Apache服务配置 2.安装cpolar内网穿透2.1 注册cpolar账号2.2 下载cpolar客户端 3. 获取远程桌面公网地址3.1 登录cpolar web ui管理界面3.2 创建公网地址 4. 固定公网地址 前言 Apache作为全球使用较高的Web服务器…

[React源码解析] Fiber

在React15及以前, Reconciler采用递归的方式创建虚拟Dom, 但是递归过程不可以中断, 如果组件的层级比较深的话, 递归会占用线程很多时间, 那么会造成卡顿。 为了解决这个问题, React16将递归的无法中断的更新重构为异步的可中断更新, Fiber架构诞生。 文章目录 1.Fiber的结构2…

【mysql把一个字段分割成两个字段】

需求: 用sql语句把一个字段分割成两个字段 idnameold_string1张三张三房间1 类似这样 要把old_string分割成id和name UPDATE table_name setname substr(old_string,1,2),id substr(old_string,locate(房间,old_string)1,1);locate(房间,old_string)//房间这个指定字符串…

【blender烘焙】法线烘焙出现大面积结构丢失怎么办?blender烘焙vs八猴烘焙

用dcc烘焙法线是很常用的减面优化手段&#xff0c;很多建模的dcc自己也内置的烘焙的功能&#xff0c;像我自己在工作流中也偶尔用blender的烘焙做一下材质的整合优化&#xff0c;在质量要求不高的时候还算凑合可用。 问题描述 在前期的文章中飞燕2号建模&#xff0c;我就遇到…

数据防泄密方案公司(dlp数据防泄密厂商排名)

在当今数字化时代&#xff0c;数据已经成为了企业最重要的资产之一。然而&#xff0c;随着企业信息化的不断深入&#xff0c;数据泄露的风险也越来越大。为了保护企业的核心数据&#xff0c;越来越多的企业开始重视数据防泄密工作&#xff0c;并寻求专业的数据防泄密方案提供商…

LeetCode——415. 字符串相加

C开头 &#x1f636;‍&#x1f32b;️&#x1f636;‍&#x1f32b;️&#x1f636;‍&#x1f32b;️&#x1f636;‍&#x1f32b;️Take your time ! &#x1f636;‍&#x1f32b;️&#x1f636;‍&#x1f32b;️&#x1f636;‍&#x1f32b;️&#x1f636;‍&#…

可解释性AI(XAI)的兴起

在今天这个科技迅猛发展的时代&#xff0c;人工智能&#xff08;AI&#xff09;已经渗透到我们生活的每一个角落。从智能家居到自动驾驶&#xff0c;从个性化推荐到医疗诊断——AI的决策过程在很多情况下&#xff0c;对我们的日常生活产生着重大影响。然而&#xff0c;这些复杂…

JavaSE-

1. Lambda 表达式 1.1 概述 Lambda表达式是一种没有名字的函数&#xff0c;也可称为闭包&#xff0c;是java8发布的最重要的新特性。 本质上是一段匿名内部类&#xff0c; 也可以是一段可以传递的代码&#xff0c;lambda表达式也被叫做箭头函数。 闭包: 闭包就是能够读取其它函…

如何回话与技巧

1、领导关心 谢谢领导关心&#xff0c;我从中学到了很多&#xff0c;如果有做的不好的方面&#xff0c;还请您多多指点。 2、领导问你忙不忙时 领导有什么工作您安排&#xff0c;如果急的话&#xff0c;我优先处理。 3、安排超过能力时 谢谢您的信任&#xff0c;虽然这件事对…

俄罗斯方块游戏设计文档(基于C语言)

1. 引言 本设计文档旨在详细规划基于C语言开发的俄罗斯方块游戏的整体架构、功能模块以及具体实现步骤。这款游戏将通过控制下落的几何形状方块&#xff0c;以填充和消除行的方式进行&#xff0c;旨在提供用户友好的界面与流畅的游戏体验。 2. 需求分析 2.1 核心元素 - 方块…

【计网·湖科大·思科】实验五 IPV4地址-分类地址和构建超网

&#x1f57a;作者&#xff1a; 主页 我的专栏C语言从0到1探秘C数据结构从0到1探秘Linux &#x1f618;欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收藏✍️留言 &#x1f3c7;码字不易&#xff0c;你的&#x1f44d;点赞&#x1f64c;收藏❤️关注对我真的很重要&…

CUDA编程的框架-以向量相加为例

CPU适合控制GPU程序的逻辑结构。 注意在编程时要区分CPU的程序和GPU的程序&#xff0c;CPU的内存和GPU的内存。 host- CPU device- GPU CPU的内存和GPU的内存之间是相互独立的&#xff0c;因此需要进行通信。 __global__ //核函数的声明符号<<<grid,block>>>…

【Shell实战案例面试题】输入网卡的名字,来输出网卡的IP

1.问题 参数后判断要加"" 名字为空时显示ip 2.分析 把本机的所有网卡名列出来&#xff0c;来引导用户输入 使用命令列出所有网卡信:ifconfig/ip a 设计一个函数&#xff0c;把网卡名作为参数&#xff0c;函数返回网卡的IP 在获取某个网卡IP时&#xff0c;考虑网…

【C语言/数据结构】排序(快速排序及多种优化|递归及非递归版本)

&#x1f308;个人主页&#xff1a;秦jh__https://blog.csdn.net/qinjh_?spm1010.2135.3001.5343&#x1f525; 系列专栏&#xff1a;《数据结构》https://blog.csdn.net/qinjh_/category_12536791.html?spm1001.2014.3001.5482 ​​​​ 目录 交换排序 快速排序 hoare版代…

【Git】Conventional Commit提交规范

Conventional Commit提交规范 说明 在Git规范中&#xff0c;提交信息&#xff08;commit message&#xff09;通常按照某个约定来编写&#xff0c;以提供更多上下文&#xff0c;帮助团队成员理解每次提交的目的。一种广泛使用的约定是Conventional Commits规范&#xff0c;它…

Windows Server 2025 LTSC 预览版来了

Windows Server 2025 LTSC 预览版来了 1. 安装 Windows Server 2025 LTSC 预览版2. 安装 VMware Tools3. Windows Server 2025 LTSC 预览版4. Windows Server 2025 LTSC 预览版下载地址 1. 安装 Windows Server 2025 LTSC 预览版 使用 VMware Workstation 安装&#xff0c; 安…

推特账号被冻结怎么办?检查IP是否正常

Twitter 拥有庞大的用户群和日常内容流&#xff0c;是沟通、网络和营销的重要平台。然而&#xff0c;处理其限制和潜在的帐户问题可能很棘手。有许多跨境社媒小伙伴反馈&#xff0c;账号无故被冻结&#xff0c;导致内容与客户尽失&#xff01;其实除了账户养号、被举报、广告信…