【C++】类和对象④(类的默认成员函数:取地址及const取地址重载 | 再谈构造函数:初始化列表,隐式类型转换,缺省值)

🔥个人主页:Forcible Bug Maker

🔥专栏:C++

目录

前言

取地址及const取地址操作符重载

再谈构造函数

初始化列表

隐式类型转换

explicit关键字

成员变量缺省值

结语


前言

本篇主要内容:类的六个默认成员函数中的取地址const取地址重载构造函数初始化列表隐式类型转换缺省值

上篇博客用之前学过的知识实现了一个简单的日期类Date,在日期类中,有介绍到多种类型运算符重载的运用,如前置++后置++等。在运算符重载的过程中,有效的代码复用也非常重要,可以大大简化代码编写过程。最后还提到了const成员和友元。本篇博客将会介绍最后两个类的默认成员函数,不过并不困难。而文中再次谈到的构造函数需要静下心来理解。

取地址及const取地址操作符重载

这两个默认成员函数一般不用重新定义,编译器默认生成的就够用。

class Date
{
public:Date(int year = 2000, int month = 1, int day = 1){_year = year;_month = month;_day = day;}// 取地址重载Date* operator&(){return this;}// const取地址重载const Date* operator&()const{return this;}
private:int _year; // 年int _month; // 月int _day; // 日
};int main()
{Date d1;const Date d2;cout << &d1 << endl;cout << &d2 << endl;return 0;
}

取地址重载,其实就是返回地址的两个函数,C++提供这种默认成员函数主要是想兼容操作符重载,给予C++更大的灵活性。在上面的代码案例中,d1取地址时调用的是非const类型的取地址重载函数,而d2取地址时调用的是const类型的取地址重载函数。我们可以改变返回值再去观察一下。

这次我们调整返回值后再打印,是否能感受到关于取地址重载的运用呢?其实,取地址重载很少用,除非你要恶作剧或者想让别人获取到指定的内容,否则默认生成的取地址就是完全够用的

再谈构造函数

初始化列表

C++的初始化列表(Initializer List)是构造函数的一种特性,用于初始化类的数据成员。在构造函数体执行之前,初始化列表会先执行,确保数据成员在构造函数体开始执行之前就已经被正确地初始化

初始化列表的使用:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个“成员变量”后面跟一个放在括号中的初始值或表达式

class Date
{
public:Date(int year, int month, int day): _year(year), _month(month), _day(day){}
private:int _year;int _month;int _day;
};

当使用上述类初始化对象时,三个成员函数都会成功在初始化列表中被传入的参数初始化。你可能会问,为什么要有初始化列表,在构造函数的函数体中初始化不是很香吗?可以来看看下面这个例子:

class stack
{
public:stack(int capacity = 4){_a = (int*)malloc(sizeof(int) * capacity);_size = 0;_capacity = capacity;}void push(int x){_a[_size++] = x;}private:int* _a;int _size;int _capacity;
};
class MyQueue
{
public:MyQueue(int pushN, int popN){}
private:stack _pushst;stack _popst;int _size;
};

在上面这份代码中,我们编写了一个MyQueue类,里面定义了两个对象成员和一个整型成员变量,你是否想过,该如何初始化对象成员呢由于stack中定义了缺省参数,不需要传参就可以完成构造但如果你需要指定stack的capacity或者没有缺省参数时,该怎么办呢

仔细思考,进入函数体后,成员变量的空间就已经都开好了,所以在函数体中是无法完成初始化赋值的。而初始化列表就可以完美解决此问题。如下是初始化列表初始化对象的方式:

以下三种类的成员,必须放在初始化列表的位置进行初始化

  • 引用成员变量
  • const成员变量
  • 自定义类型成员(且没有默认构造函数)
class A
{
public:A(int a):_a(a){}
private:int _a;
};
class B
{
public:B(int a, int& ref):_aobj(a), _ref(ref), _n(10){}
private:A _aobj; // 没有默认构造函数int& _ref; // 引用const int _n; // const
};

建议:能在初始化列表中初始化就在初始化列表中初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化。

初始化列表的特点

  1. 初始化列表,不管写没写,每个成员变量都会走一遍而且在初始化列表中只能出现一次(初始化只能初始化一次)
  2. 对于自定义类型,会调用默认构造(没有默认构造则报错)。
  3. 先走初始化列表,再走函数体
  4. 拷贝构造也有初始化列表
  5. 成员变量在类中声明次序就是其在初始化列表中的初始化顺序与其在初始化列表中的先后次序无关

对于第四点,我们可以使用一份代码来证明:

上面这份代码,根据_a1和_a2的声明顺序,初始化列表先走的_a2,再走的_a1,导致在初始化_a2使用了未初始化_a1,故产生了随机值,佐证了特点四。

隐式类型转换

之前我们讲过,不同类型的内置类型变量在相互赋值时会有隐式类型转换

double a = 10.5;
int b = a;

就如上面这个简单的赋值,在a赋值给b之前,会产生一个临时变量,最终赋给b值的就是这个临时变量。

当将不同类型的变量取引用时,需要加const的原因,是因为临时变量具有常性。

临时变量具有常性,其本质就跟数字一样如,1,2,3等,可以给变量赋值,正常情况下不能取到地址或者取到引用,除非用const修饰变量。

double a = 10.5;
// int& b = a;// 报错
// int& c = 10;// 报错
const int& b = a;// 正常运行
const int& c = 10;// 正常运行

上述代码中b取的就是a产生的临时变量的引用临时变量存储在内存的静态区,具有常性,就跟第四行代码的数字10性质是一样的,当你加上const时,这种引用权限就被放开了,因为const确保了你不会对静态区的变量做出改动。对于C++的自定义类型,与内置类型遵循的规则是一样的。

C++支持一种类型转换式的构造:

class A
{
public:A(int a):_a1(a){}A(const A& aa):_a1(aa._a1){cout << "A(const A& aa)" << endl;}
private:int _a1;int _a2;
};
int main()
{A aa1(1);A aa2 = 1;return 0;
}

对于main函数第一行代码是标准的调用了构造函数。而第二行,作为内置类型的1,竟然能给对象的初始化赋值,这是因为在赋值之前,产生了隐式类型转换1作为一个参数传递给了构造函数从而产生了一个临时对象,最终临时变量拷贝构造给aa2

在调用此代码的过程中,我们发现,并没有调用拷贝构造函数,这是因为通过编译器的优化省去了拷贝构造这一过程,简单来说就是:

构造函数 + 拷贝构造 + 编译器优化 = 构造函数

这时候看这两行能否运行的原因应该就不困难了:

// A& ref = 10;// 报错
const A& ref = 10;//可运行
// 这里ref引用的是类型转换中用10构造的临时对象

在上面代码中,我们使用的构造函数一直是单参数的,可以使用特殊的隐式类型转换构造。但是如果构造函数是多参数的,该怎么使用类似于A aa = 1;的方式创建对象呢?其实C++提供了解决方案,那就是多参数构造

class A
{
public:A(int a1, int a2):_a1(a1), _a2(a2){}A(const A& aa):_a1(aa._a1),_a2(aa._a2){}void print()const{cout << _a1 << " " << _a2 << endl;}
private:int _a1;int _a2;
};
// 多参数构造
int main()
{A aa1 = { 2,2 };aa1.print();// A& ref = { 2,3 };//报错const A& ref = { 2,3 };ref.print();return 0;
}

不过需要注意的是,只有C++11及其往后的版本才支持多参数构造。老版本,如C++98并不支持这样创建对象。

explicit关键字

这个知识点稍稍提一下,如果不想允许构造时出现类的隐式类型转换,可以在拷贝构造前加个explicit关键字,就可以成功限制类的隐式类型转换了。

关于explicit的更多使用,在后面有机会还会讲。

成员变量缺省值

之前讲过,在C++11的新标准中,支持为类中的成员变量提供缺省值。在类和对象中,提供的缺省值是提供给初始化列表使用的由于支持隐式类型转换构造等原因提供的缺省值可以非常灵活,见代码:

class A
{
public:A(int a1):_a1(a1){cout << "A(int a1)" << endl;}A(int a1, int a2):_a1(a1), _a2(a2){cout << "A(int a1, int a2)" << endl;}A(const A& aa):_a1(aa._a1), _a2(aa._a2){cout << "A(const A& aa)" << endl;}
private:int _a1;int _a2;
};
class B
{
public:
private:int _b1 = 1;// 缺省值可以给整型变量int* ptr = (int*)malloc(40);// 可以开空间给指针A _aa1 = 1;// 可以给对象类型(A _aa1(1);这样构造是错误的)A _aa2 = { 1,2 };// 多参数构造A _aa3 = _aa2;// 拷贝构造,缺省参数甚至可以是一个对象
};
int main()
{B bb1;return 0;
}

这些缺省参数,最终都会提供给初始化列表

如果显示提供了初始化列表,运行时,这些被提供的缺省参数就会被忽略(简单说就是:如果既提供了初始化列表,也有缺省值,编译器默认使用初始化列表提供的值)。

结语

本篇博客将最后两个默认成员函数做了一个收尾,再次谈到了构造函数的一些语法和特性,关于初始化列表的概念和使用;一种很新的创建对象方式,隐式类型转换方式创建对象,而explicit关键字可以限制这种转换的发生;最后还提到了C++11的新特性成员变量的缺省值,列出了对象,指针等类型给缺省值的方式。在类和对象的下一篇,会再介绍几个类和对象的小特性,以及编译器做出的优化。

博主还会继续产出有趣的内容,感谢大家的支持!♥

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

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

相关文章

Stable Diffusion 模型分享:_CHEYENNE_(欧美漫画)CHEYENNE_v16.safetensors

本文收录于《AI绘画从入门到精通》专栏,专栏总目录:点这里,订阅后可阅读专栏内所有文章。 文章目录 模型介绍生成案例案例一案例二案例三案例四案例五案例六案例七案例八下载地址模型介绍<

吉林省教育学院学报杂志社吉林省教育学院学报编辑部2024年第3期目录

特稿《吉林省教育学院学报》投稿&#xff1a;cn7kantougao163.com 吉林省2023年初中毕业学业水平考试评价与分析报告 Junior High School Teaching Research and Training Department, Jilin Provincial Institute of Education; 1-25 基于吉林省图书馆专利数据资源的吉…

刷题训练之二分查找

> 作者&#xff1a;დ旧言~ > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 目标&#xff1a;熟练掌握二分查找算法 > 毒鸡汤&#xff1a;学习&#xff0c;学习&#xff0c;再学习 ! 学&#xff0c;然后知不足。 > 专栏选自&#xff1a;刷题…

解决“找不到MSVCP120.dll”或“MSVCP120.dll丢失”的错误方法

在计算机使用过程中&#xff0c;遇到诸如“找不到MSVCP120.dll”或“MSVCP120.dll丢失”的错误提示并不罕见。这类问题往往会导致某些应用程序无法正常运行&#xff0c;给用户带来困扰。本文旨在详细阐述MSVCP120.dll文件的重要性、其丢失的可能原因&#xff0c;以及解决方法&a…

C++ //练习 12.32 重写TextQuery和QueryResult类,用StrBlob代替vector<string>保存输入文件。

C Primer&#xff08;第5版&#xff09; 练习 12.32 练习 12.32 重写TextQuery和QueryResult类&#xff0c;用StrBlob代替vector保存输入文件。 环境&#xff1a;Linux Ubuntu&#xff08;云服务器&#xff09; 工具&#xff1a;vim 代码块 /*****************************…

Jammy@Jetson Orin - Tensorflow Keras Get Started: 000 setup for tutorial

JammyJetson Orin - Tensorflow & Keras Get Started: 000 setup for tutorial 1. 源由2. 搭建环境2.1 安装IDE环境2.2 安装numpy2.3 安装keras2.4 安装JAX2.5 安装tensorflow2.6 安装PyTorch2.7 安装nbdiff 3. 测试DEMO3.1 numpy版本兼容问题3.2 karas API - model.compil…

STC15L2K60S2-28I-LQFP44 单片机芯片 STC宏晶

STC15L2K60S2-28I-LQFP44 规格信息&#xff1a; 产品类型STC(宏晶) UART/USART2 额定特性- SPI1 USB Device0 USB Host/OTG0 PWM3 I2C&#xff08;SMBUS/PMBUS&#xff09;0 LCD0 工作电压2.4V ~ 3.6V EEPROM 尺度1KB Ethernet0 A/D8x10bit CAN0 D/A3x10bit CPU…

【VI/VIM】基本操作备忘录

简介 新建/打开文件 工作模式 常用命令 补全命令 命令模式输入&#xff1a;ctrl p 移动命令 文本选中 撤销、删除 复制粘贴 替换 缩排 查找 替换 插入 分屏 练习

Spectre-v2 以及 Linux Retpoline技术简介

文章目录 前言一、Executive Summary1.1 Spectre-v2: Branch Predictor Poisoning1.2 Mitigating Spectre-v2 with Retpolines1.3 Retpoline Concept 二、BackgroundExploit Composition 三、(Un-)Directing Speculative Execution四、Construction (x86)4.1 Speculation Barri…

Linux文件权限核心知识

1.1 权限概念 Linux 里面不同 用户 对不同 文件、目录、用户 等对象的控制能力。 1.2 权限属性 ##创建文件 [rootoldboyedu ~]# touch oldboy.txt [rootoldboyedu ~]# ls -l oldboy.txt -rw-r--r-- 1 root root 14 9月 26 10:22 oldboy.txt ##创建目录 [rootoldboyedu ~]# mk…

项目上线流程(保姆级教学)

01&#xff1a;注册阿里云账户 02&#xff1a;登录阿里云 03&#xff1a;在桌面新建记事本保存个人账号密码等信息 04&#xff1a;完成重置密码 05&#xff1a;安装宝塔面板 命令行 yum install -y wget && wget -O install.sh http://download.bt.cn/install/instal…

数据结构之顺序表的实现(C语言版)

Hello, 大家好&#xff0c;我是一代&#xff0c;今天给大家带来有关顺序表的有关知识 所属专栏&#xff1a;数据结构 创作不易&#xff0c;望得到各位佬们的互三呦 一.前言 1.首先在讲顺序表之前我们先来了解什么是数据结构 数据结构是由“数据”和“结构”两词组合⽽来。 什…

Android集成Sentry实践

需求&#xff1a;之前使用的是tencent的bugly做为崩溃和异常监控&#xff0c;好像是要开始收费了&#xff0c;计划使用开源免费的sentry进行替换。 步骤&#xff1a; 1.修改工程文件 app/build.gradle apply plugin: io.sentry.android.gradle sentry {// 禁用或启用ProGua…

将彩色图转化为灰度图及其原理介绍

彩色图介绍 彩色图像是一种包含颜色信息的图像&#xff0c;通常由红色、绿色和蓝色&#xff08;RGB&#xff09;三个颜色通道组成。这三种颜色通道可以叠加在一起来形成各种不同的颜色。 彩色图像中的每个像素都有三个数值&#xff0c;分别表示红色、绿色和蓝色通道的强度或亮…

【数据结构(邓俊辉)学习笔记】绪论04——算法分析

文章目录 0. 前言1. 算法分析2.级数2.1基本形式2.2 收敛级数 3.循环 vs 级数4.示例 0. 前言 通过以基本计算模型作为参照&#xff0c;并且以大O记号的形式在上面添加适当刻度&#xff0c;已经建立一套对DSA进行分析的完整工具和体系。不清楚的可以看看复杂度度量 、复杂度分析…

Mybatisplus LambdaQueryWrapper表达式使用DATE_FORMAT比较日期函数

背景&#xff1a; 最近遇到一个问题&#xff0c;数据库保存的日期字段是如下格式 但是我们需要比较的日期为 2020-08-01格式&#xff0c; 所以我们要将日期格式化 使用 Mybatisplus LambdaQueryWrapper的情况下可用下面的方式做参考 LambdaQueryWrapper<SysDicCode> la…

代码随想录算法训练营DAY35|C++贪心算法Part.4|860.柠檬水找零、406.根据身高重建队列、452. 用最少数量的箭引爆气球

文章目录 860.柠檬水找零伪代码实现CPP代码 406.根据身高重建队列思路伪代码实现代码优化 CPP代码 452. 用最少数量的箭引爆气球思路伪代码实现CPP代码 860.柠檬水找零 力扣题目链接 文章讲解&#xff1a;860.柠檬水找零 视频讲解&#xff1a;贪心算法&#xff0c;看上去复杂&a…

Windows系统部署Emby影音服务并实现无公网IP访问本地影视资源

文章目录 1.前言2. Emby网站搭建2.1. Emby下载和安装2.2 Emby网页测试 3. 本地网页发布3.1 注册并安装cpolar内网穿透3.2 Cpolar云端设置3.3 Cpolar内网穿透本地设置 4.公网访问测试5.结语 1.前言 本文主要介绍如何在Windows系统中&#xff0c;使用Cpolar内网穿透Emby&#xf…

C++入门(3)

文章目录 C入门auto同一行中定义多个变量auto不能推到的场景基于范围的for循环(C11)10. 指针空值nullptr(C11) C入门 auto auto&#xff1a;C11中&#xff0c;标准委员会赋予了auto全新的含义即&#xff1a;auto不再是一个存储类型指示符&#xff0c;而是作为一个新的类型指示…

linux信号机制分析

概念 信号递达&#xff1a;实际执行信号的处理动作就是信号递达 信号未决&#xff1a;信号从产生到递达之间的状态就是信号未决&#xff08;未决就是没有解决&#xff09; 收到某信号后&#xff0c;把未决信号集中的此信号置为1&#xff08;1表示未解决的信号&#xff09;&a…