【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的新特性成员变量的缺省值,列出了对象,指针等类型给缺省值的方式。在类和对象的下一篇,会再介绍几个类和对象的小特性,以及编译器做出的优化。

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

RK3568 学习笔记 : u-boot 千兆网络功能验证

前言 开发板型号&#xff1a; 【正点原子】 的 RK3568 开发板 使用 虚拟机 ubuntu 20.04 编译 RK3568 Linux SDK&#xff0c;生成镜像&#xff0c;烧写后&#xff0c;Linux 系统正常启动 开启后可以使用 CTRLC 进入 u-boot 本篇验证一下 u-boot 下网络功能 【正点原子】 rk…

TMS运输管理系统:开启高效物流之门的钥匙

TMS运输管理系统是一种集货运计划、路径规划、运输执行和跟踪管理于一体的综合管理系统。它利用现代信息技术和互联网资源&#xff0c;帮助企业高效管理供应链&#xff0c;提高物流效率和降低物流成本。本文将从系统优势、功能模块和应用案例等多个方面详细介绍TMS运输管理系统…

PHP校验15位和18位身份证号

第十八位数字的计算方法为&#xff1a; 1.将前面的身份证号码17位数分别乘以不同的系数。从第一位到第十七位的系数分 别为&#xff1a;7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2 2.将这17位数字和系数相乘的结果相加。 3.用加出来和除以11&#xff0c;看余数是多少&#xff1f; 4…

ESP32-Thonny 拍摄图片到SD卡

前言&#xff1a; 代码运行在Thonny 添加main.py到单片机中&#xff1a; 可以先运行一下试试&#xff1a;会输出以下信息&#xff1a; 没有问题的话&#xff08;SD卡挂载成功&#xff0c;摄像头初始化成功&#xff09;运行一次主程序后&#xff0c;闪光灯会闪烁一下。 代码&…

React首次加载渲染2次的问题

在开发React项目的时候&#xff0c;发现useEffect会调用2次的情况&#xff0c;依赖数组明明没有变化&#xff0c;怎么会调用2次&#xff1f;百思不得其解&#xff0c;依赖没变化的话&#xff0c;那肯定是整个组件重渲染了。 最最简单的代码如下&#xff1a; const container …

Python | Leetcode Python题解之第41题缺失的第一个正数

题目&#xff1a; 题解&#xff1a; class Solution:def firstMissingPositive(self, nums: List[int]) -> int:n len(nums)for i in range(n):while 1 < nums[i] < n and nums[nums[i] - 1] ! nums[i]:nums[nums[i] - 1], nums[i] nums[i], nums[nums[i] - 1]for …

vue实现水平排列且水平居中

样式实现 .body{text-align: center; } .body_content{display: inline-block; } .body_content_cardList{display: flex;flex-wrap: wrap;text-align: center; }<div class"body"><div class"body_content"><div class"body_content…

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

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

以始为终梳理前端的发展方向

嗨&#xff0c;我是小路。一位努力向上生长的90后前端开发工程师。 以下是正文&#xff1a; 前段时间朋友和我吐槽&#xff1a;“做了多年的PHP开发&#xff0c;突然被离职&#xff0c;然后去招聘市场一看&#xff0c;发现PHP已经没有市场了。偶尔会出现一两个相关的职位&#…

因果推断(三):causalml的使用(1)_元学习器的使用

元学习器是利用一些现成的机器学习方法来进行因果推断的方法。也是相对来说最简单的进行因果推断的模型&#xff0c;在econml和causalml都有实现&#xff0c;调用也相对比较方便。 1.1. S_Learner S 指的是 single&#xff0c;在S_Learner中&#xff0c;只需要训练一个机器学…

贪吃蛇游戏C语言破解:成为编程高手的必修课!

​ 个人主页&#xff1a;秋风起&#xff0c;再归来~ 文章专栏&#xff1a;C语言实战项目 个人格言&#xff1a;悟已往之不谏&#xff0c;知来者犹可追 克心守己&#xff0c;律己则安&#xff01; 1、游戏效果演示 贪吃蛇游戏效果演示 2、win32 A…

【深度学习实战(20)】使用torchsummary打印模型结构

一、安装torchsummary库 pip install torchsummary 二、代码 import torchvision.models as models from torchsummary import summarymodel models.AlexNet() model.to(cuda) summary(model,(3,224, 224))

AI智能边缘分析一体机,32T算力,可同时处理32路1080p高清视频

产品概述 XM-AIBOX-32智能边缘分析一体机是一款高性能、低功耗边缘计算产品。搭载BM1684X主芯片&#xff0c;INT8算力高达32TOPS&#xff0c;FP16/BF16算力高达16TFLOPS&#xff0c;FP32算力高达2TFLOPS&#xff0c;可同时处理32路高清视频&#xff0c;支持32路1080P高清视频硬…

【NOI】C++算法设计入门之深度优先搜索

文章目录 前言一、深度优先搜索1.引入2.概念3.迷宫问题中的DFS算法步骤4.特点5.时间、空间复杂度5.1 时间复杂度 (Time Complexity)5.2 空间复杂度 (Space Complexity)5.3 小结 二、例题讲解1.问题&#xff1a;1586 - 扫地机器人问题&#xff1a;1430 - 迷宫出口 三、总结四、感…

docker安装并跑通QQ机器人实践(3)-bs-nonebot搭建

NoneBot2 是一个现代、跨平台、可扩展的 Python 聊天机器人框架&#xff08;下称 NoneBot&#xff09;&#xff0c;它基于 Python 的类型注解和异步优先特性&#xff08;兼容同步&#xff09;&#xff0c;能够为你的需求实现提供便捷灵活的支持。同时&#xff0c;NoneBot 拥有大…

Rust实战 | 用 RustRover 开发猜数字游戏

#1、概述 从这期开始&#xff0c;我会以实际项目的形式&#xff0c;分享个人学习 Rust 语言的过程。期间&#xff0c;我不会讲太多语法知识&#xff0c;需要各位看官自行查阅文档&#x1f604;。 开始前&#xff0c;需具备 Rust 环境&#xff08;我用的是当前最新版本1.77.2&…

【Transformer】detr之encoder逐行梳理(二)

every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 detr之encoder逐行梳理 1. 整体 encoder由encoder layer构成 输入进encoder的特征shape:(hw,b,c)&#xff0c;后文将给出说明 class Transformer(nn.…

Nacos分布式配置中心

<?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.0.0 https://…

常见的数据抽取工具对比

1.什么是ETL? ETL&#xff0c;是英文Extract-Transform-Load的缩写&#xff0c;用来描述将数据从来源端经过抽取&#xff08;extract&#xff09;、转换&#xff08;transform&#xff09;、加载&#xff08;load&#xff09;至目的端的过程&#xff0c;是数据仓库的生命线。 …

C#仿QQ抽屉式窗体的设计方法:创建特殊窗体

目录 1.WindowFromPoint函数 2.GetParent函数 3.实例 &#xff08;1&#xff09; 图片集合编辑器 &#xff08;2&#xff09;Form1.Designer.cs &#xff08;3&#xff09;Form1.cs 4.生成效果 QQ软件对于绝大多数的人来说再熟悉不过了&#xff0c;它以使用方便、界面美…