C++——类和对象(初始化列表、匿名对象、static成员、类的隐式类型转换和explicit关键字、内部类)

news2024/11/15 20:11:50

初始化列表、匿名对象、static成员、类的隐式类型转换和explicit关键字、内部类

本章思维导图:

在这里插入图片描述

注:本章思维导图对应的xmind文件和.png文件都已同步导入至资源

文章目录

  • 初始化列表、匿名对象、static成员、类的隐式类型转换和explicit关键字、内部类
  • 1. 初始化列表
    • 1.1 再谈构造函数
    • 1.2 初始化列表
      • 1.2.1 初始化列表的语法
      • 1.2.2 初始化列表的意义
    • 1.3 注意事项
  • 2. 匿名对象
  • 3. static成员
    • 3.1 static成员变量
    • 3.2 static成员函数
  • 4. 类的隐式类型转换和explicit关键字
    • 4.1 类的隐式类型转换
    • 4.1 explicit关键字
  • 5. 内部类

1. 初始化列表

在这里插入图片描述

1.1 再谈构造函数

众所周知,每个变量只能被初始化一次,我们之前一直认为成员变量的初始化是在构造函数的函数体中,但是,成员变量是可以在构造函数的函数体出现多次的

class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
		//出现多次,且可以编译通过
		_year = 100;
		_month = 200;
	}
private:
	int _year;
	int _month;
	int _day;
};

因此,我们只能认为在构造函数函数体内执行的是赋值操作,而不是初始化

这就说明,构造函数的函数体并不是类的成员变量真正初始化的地方,那么成员变量到底是在哪里初始化的呢?

1.2 初始化列表

初始化列表是成员变量真正初始化的地方

1.2.1 初始化列表的语法

初始化列表以分号:开始,以逗号,分割,每个成员变量后面带上放在括号()里的初始值或者表达式

例如,对于上面的构造函数:

Date(int year = 1, int month = 1, int day = 1)
		//初始化列表
    : _year(year)
    , _month(month)
    , _day(day)
{

}

1.2.2 初始化列表的意义

初始化列表解决了三类不能在构造函数的函数体内初始化的问题

  • &修饰的引用成员变量——引用成员在定义时就必须初始化
  • const修饰的const成员变量——const变量在定义时就必须初始化
  • 没有默认构造的自定义类型——在函数体内不能初始化自定义类型

也就是说,上面所说三类成员变量必须在初始列表里面进行初始化

例如;

class Stack
{
public:
    //这不是默认构造,因为要传参数
	Stack(int capacity)
	{

	}

private:
	int* _a;
	int _capacity;
	int _top;
};

class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
		: num1(2)
		, num2(_year)
		, st(3)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	
	int _year;
	int _month;
	int _day;
	
	const int num1;
	int& num2;
	Stack st;

};

1.3 注意事项

  • 因为初始化列表是成员变量初始化的地方,而每个变量又只能初始化一次,因此成员变量只能在初始化列表出现一次

  • 因为初始化列表是真正初始化成员变量的地方,因此无论有没有显示的写出初始化列表,成员变量都会经过初始化列表的初始化

  • 如果没有显示的写出初始化列表,那么:

    • 对于内置类型,那就赋予其初始值
    • 对于自定义类型,就调用它的默认构造
  • 能使用初始化列表就使用初始化列表。但也不是说初始化列表就能完全替代函数体。因为有时候函数体需要进行检查等操作。

  • 初始化列表的初始化顺序是成员变量声明的顺序,而不是在初始化列表里出现的顺序。

    class A
    {
    public:
    	A()
    		: a1(1)
    		, a2(a1)
    	{
    	}
    
    	void Print()
    	{
    		cout << a1 << endl << a2 << endl;
    	}
    private:
    	int a2;
    	int a1;
    };
    
    int main()
    {
    	A a;
    	a.Print();
    
    	return 0;
    }
    
    /*output:
    	1
    	-858993460
    */
    //a2声明在a1之前,因此,在初始化时,先执行a2(a1),此时a1为随机值
    //因此建议成员变量的初始化顺序和声明顺序一致
    

2. 匿名对象

在这里插入图片描述

class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{
		myCount++;
	}

	void Print()
	{
		cout << "Date" << endl;
	}
private:
	
	int _year;
	int _month;
	int _day;
};

如果我们想不实例化对象,但想调用Date类里的Print()函数来知道这是个什么类,该如何做到呢?

这里就可以用到我们的匿名对象来解决:

int main()
{
	Date().Print();	//Date()创建出一个匿名对象,再用这个匿名对象来调用成员函数Print()

	return 0;
}

创建匿名对象的方式:

className()

匿名对象的特点:

  • 匿名对象是一种临时对象,它没有分配给任何命名变量,而是在需要时被创建并使用
  • 其生命周期仅存在于当前行,执行完后立即销毁
  • 匿名对象一般是常量对象,不可被修改

3. static成员

在这里插入图片描述

如果我们想记录一个类究竟被构造了多少次

我们不难写出这样的代码:

//定义一个全局变量来记录类A构造的次数
int myCount = 0;

class A
{
public:
	//构造函数
	A()
	{
		myCount++;
	}
	//拷贝构造
	A(A& a)
	{
		myCount++;
	}
};

int main()
{
	A a[10];

	cout << myCount << endl;

	return 0;
}

但是这就出现了一个问题:我们可以在全局随意修改变量myCount的值:

int main()
{
	A a[10];

	myCount++;

	cout << myCount << endl;

	return 0;
}

这样也就不能保证类被构造次数的正确性了。

3.1 static成员变量

为了解决这个问题,我们可以在类里面声明一个static成员,并用这个成员来记录类被构造的次数

class A
{
public:
	//构造函数
	A()
	{
		myCount++;
	}
	//拷贝构造
	A(A& a)
	{
		myCount++;
	}

private:
	static int myCount;
};

int A::myCount = 0;

这个static修饰的静态成员变量有如下特点

  • 实际上也是一个全局变量,只是受类域和访问限定符所限制
  • 静态成员变量只能在类里面声明在类外面定义。
  • 静态成员变量在声明时不能和非静态成员变量一样给缺省值,因为这个缺省值是给初始化列表里用的,而静态成员变量不用初始化列表初始化。
  • static修饰的静态成员变量是这个类所属的,而不是由这个类实例化的某个对象所独有

3.2 static成员函数

知道如何利用static成员变量之后,针对最开始的问题,我们不难写出下面的代码:

class A
{
public:
	//构造函数
	A()
	{
		myCount++;
	}
	//拷贝构造
	A(A& a)
	{
		myCount++;
	}

	//因为myCount被private修饰,在类外面无法访问
	//因此要用成员函数访问myCount
	int GetCount()
	{
		return myCount;
	}

private:
	static int myCount;
};

int A::myCount = 0;

int main()
{
	A a[10];

	//为了调用GetCount成员函数,必须要实例化一个对象,而这个对象是没有意义的,因此最终结果要减一
	cout << A().GetCount() - 1 << endl;
	return 0;
}

但是又有一个问题出现了:

我们只是想知道A类到底被调用了多少次,但是要知道这个结果又必须新实例化一个对象,有没有什么方法不实例化对象就可以直接得到myCount的值呢?

为了解决上述问题,就需要用到static成员函数

class A
{
public:
	//构造函数
	A()
	{
		myCount++;
	}
	//拷贝构造
	A(A& a)
	{
		myCount++;
	}

	//static静态成员函数
	static int GetCount()
	{
		return myCount;
	}

private:
	static int myCount;
};

int A::myCount = 0;

int main()
{
	A a[10];

	cout << A::GetCount() << endl;

	return 0;
}

static修饰的静态成员函数有如下特点

  • 和静态成员变量一样,静态成员函数实际上也是一个全局函数,只是受类域和访问限定符限制

  • 静态成员函数在类里面声明,但既可以在类外面定义也可以在类里面定义

  • 和非静态成员函数不同,静态成员函数没有this指针,因此静态成员函数无法访问非静态成员变量和非静态成员函数,但也因如此,它可以直接通过类名和域作用限定符::调用

在这里插入图片描述

4. 类的隐式类型转换和explicit关键字

4.1 类的隐式类型转换

以前我们一般是这么实例化一个对象的:

class Date
{
public:

private:
};

int main()
{
	Date d1;	//利用构造函数实例化对象
	Date d2(d1);	//利用拷贝构造实例化对象

	return 0;
}

现在又有一个新的实例化对象的方法——类的隐式类型转换

class A
{
public:
	A(int a = 1)
		: _a(a)
	{

	}

private:
	int _a;
};
int main()
{
	A A1 = 10;

	return 0;
}

在这里插入图片描述

可以看出,整形10确实被转换为了A类型。

根据当隐式类型转换发生时会产生临时变量的知识点,我们可以推导出A A1 = 10这行代码的具体实现逻辑:

在这里插入图片描述

应该清楚,要支持这种隐式类型转换,该类的构造函数应该支持只传一个内置类型就可以实现构造

例如对于下面几种情况,就不支持内置类型隐式转换为类类型:

//Error_1
class A
{
public:
	A()
		: _a(a)

	{

	}

private:
	int _a;
};
int main()
{
	A A1 = 10;

	return 0;
}
/*
报错:
 	error C2065: “a”: 未声明的标识符
 	error C2440: “初始化”: 无法从“int”转换为“A”
	message : 无构造函数可以接受源类型,或构造函数重载决策不明确
*/

//Error_2
class A
{
public:
	A(int a, int b)
		: _a(a)

	{

	}

private:
	int _a;
	int _b;
};
int main()
{
	A A1 = 10;

	return 0;
}
/*
报错:
	error C2440: “初始化”: 无法从“int”转换为“A”
 	message : 无构造函数可以接受源类型,或构造函数重载决策不明确
*/

类似的,对于有多个形参的构造函数,我们也可以传入多个内置类型进行构造:

class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{

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

int main()
{
	Date d3 = {2023, 11, 7};	//传入三个内置类型进行构造

	return 0;
}

在这里插入图片描述

4.1 explicit关键字

有些时候,如果我们不想让上面所说的隐式类型转换发生,我们可以在构造函数的声明前加上explicit关键字:

explicit Date(int year = 1, int month = 1, int day = 1)
    : _year(year)
        , _month(month)
        , _day(day)
    {

    }

加上explicit关键字后,如果继续进行隐式类型转换,就会报错:

 error C3445: "Date" 的复制列表初始化不能使用显式构造函数

5. 内部类

C++支持在类的内部继续创建类,例如:

class A
{
public:
	class B
	{

	};

private:
	int _a;
	int _b;
};

内部类有如下的特点:

  • 内部类是一个独立的类,它不属于外部类,不能通过外部类的对象来访问内部类的成员

  • 内部类天生就是外部类的友元类,可以直接访问外部类的成员变量和成员函数

    class A
    {
    public:
    	class B
    	{
    		void Print(A& a)
    		{
    			a._a = 1;
    		}
    
    	public:
    		int _b;
    	};
    
    private:
    	int _a;
    };
    
  • sizeof(外部类)的结果和内部类无关

    class A
    {
    public:
    	class B
    	{
    	public:
    		int _b;
    	};
    
    private:
    	int _a;
    };
    
    int main()
    {
    	cout << sizeof(A) << endl;
    
    	return 0;
    }
    
    //output:4
    

  • C++类和对象的知识到这里就学习完毕了,之后博主会发布C++类和对象的总结篇

  • 下一篇,博主将介绍C++的内存管理,感兴趣的小伙伴可以来看看哦~

请添加图片描述

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

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

相关文章

案例-注册页面(css)

html页面用css控制样式&#xff0c;画一个注册页面。 页面最终效果如下&#xff1a; 页面代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>注册页面</title> <style>*{…

文献阅读 - JADE:具有可选外部存档的自适应差分进化

文章目录 标题摘要关键字结论研究背景I. INTRODUCTION 常用基础理论知识II. BASIC OPERATIONS OF DEIII. ADAPTIVE DE ALGORITHMSA. DESAPB. FADEC. SaDED. jDE 研究内容、成果IV. JADEA. DE/Current-to-pbestB. Parameter AdaptationC. Explanations of the Parameter Adaptat…

WSGI与ASGI:两种Python Web服务器网关接口的比较

在当今的Web开发领域&#xff0c;选择合适的服务器网关接口&#xff08;Server Gateway Interface&#xff0c;简称SGI&#xff09;对于提高Web应用程序的性能和并发性至关重要。在Python中&#xff0c;有两种常见的SGI&#xff1a;WSGI和ASGI。本文将深入探讨这两种SGI的异同点…

中国人民大学与加拿大女王大学金融硕士——在职读研,让人生的火花迸发

每个人都像是一块未经雕琢的宝石&#xff0c;隐藏着无尽的光芒。然而&#xff0c;生活、工作中的困难、挫折和压力&#xff0c;就像尘土一样&#xff0c;掩盖了我们的闪亮之处。只有当我们冲破这些阻碍&#xff0c;才能让内在的光芒照亮世界。中国人民大学与加拿大女王大学金融…

Q-Vision+CANpro Max总线解决方案

智能联网技术在国内的发展势头迅猛&#xff0c;随着汽车智能化、网联化发展大潮的到来&#xff0c;智能网联汽车逐步成为汽车发展的主要趋势。越来越多整车厂诉求&#xff0c;希望可以提供本土的测量软件&#xff0c;特别是关于ADAS测试。而风丘科技推出的Q-Vision软件不仅可支…

一键批量剪辑:视频随机分割新玩法,高效剪辑不再难

随着视频内容的日益丰富&#xff0c;人们对于视频剪辑的需求也日益增长。而传统的视频剪辑方式往往需要耗费大量的时间和精力&#xff0c;让许多非专业人士望而却步。然而&#xff0c;现在有一款名为“云炫AI智剪”的软件&#xff0c;它为我们提供了一种全新的视频剪辑方式——…

数据结构:AVL树的旋转(平衡搜索二叉树)

1、AVL树简介 AVL树是最先发明的自平衡二叉查找树。在AVL树中任何节点的两个子树的高度最大差别为1&#xff0c;所以它也被称为高度平衡树。增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。AVL树得名于它的发明者G. M. Adelson-Velsky和E. M. Landis&#xff0c;他们…

uniapp原生插件之安卓串口操作原生插件

插件介绍 安卓串口操作原生插件&#xff0c;支持设置串口&#xff0c;波特率&#xff0c;停止位&#xff0c;数据位&#xff0c;校验位&#xff0c;流控以及延迟&#xff0c;支持粘包处理解决分包问题&#xff0c;支持多串口操作&#xff0c;无需root 插件地址 安卓串口操作…

2023年【危险化学品经营单位安全管理人员】考试资料及危险化学品经营单位安全管理人员考试试卷

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2023年危险化学品经营单位安全管理人员考试资料为正在备考危险化学品经营单位安全管理人员操作证的学员准备的理论考试专题&#xff0c;每个月更新的危险化学品经营单位安全管理人员考试试卷祝您顺利通过危险化学品经…

【深度神经网络(DNN)】实现车牌识别

文章目录 前言一、数据集介绍二、步骤1.导包2.参数配置3.数据处理4.模型定义5.模型训练6.模型预测 总结 前言 课内实践作业 车牌识别 一、数据集介绍 1.车牌识别数据集&#xff1a;VehicleLicense车牌识别数据集包含16151张单字符数据&#xff0c;所有的单字符均为严格切割且…

力扣:67.二进制求和

class Solution { public:string addBinary(string a, string b) {string ans;reverse(a.begin(), a.end()); // 将字符串a反转reverse(b.begin(), b.end()); // 将字符串b反转int n max(a.size(), b.size()), carry 0; // 获取a和b的最大长度&#xff0c;并初始化进位为0for…

Python合并拼接图片

目录 图片二维合并拼接&#xff08;类似九宫格&#xff09;图片纵向合并拼接举例18张图片合并为2张九宫格图片18张图片合并为2张纵向图片 使用前需要安装PIL库&#xff0c;以下代码使用的Pillow(10.1.0) pip install pillow图片二维合并拼接&#xff08;类似九宫格&#xff09…

趣玩行为商城:用智能消费行为开启财富新生活!

随着当前的消费主力转移至90后和Z时代&#xff0c;购物和消费习惯已经发生了翻天覆地的变化。以往那种大超市&#xff0c;线下大卖场、超级Mall综合体逐渐式微&#xff0c;以“健康生活”、“智能消费”“社区直达”为主的消费理念逐渐兴盛&#xff0c;消费者开始更多地关注此类…

sqli-labs-1

文章目录 Less-01Less-02Less-03Less-04 Less-01 1.输入不同的id值&#xff0c;可以获取不同的用户信息&#xff1a; 2.在sql后拼接’or11–&#xff0c;并没有获取到所有用户的信息&#xff0c;猜测可能用了limit语句 3.构造错误的sql语句&#xff0c;果然有limit报错: …

简历考察点1_《基于 VUE2.0 前后端分离的个人博客系统》

项目名称&#xff1a;《基于 Vue2.0①前后端分离的个人博客系统》 项目描述&#xff1a;提供最新技术资讯、开发知识和分享的博客平台&#xff0c;功能模块包括&#xff1a;支持不同用户登录注册、编辑并发布博客、上传图片、评论留言、根据访问量查询最热文章和标签、根据日期…

VM虚拟机逆向---[羊城杯 2021]Babyvm 复现【详解】

文章目录 前言题目分析汇编脚本分析汇编exp 后言 前言 无 题目分析 &unk_804B0C0里面是opcode&#xff0c;sub_1C8里面有个mprotect&#xff0c;用了一个SMC加密。 我使用的是动态调试&#xff0c;因为是ELF文件&#xff0c;链接一下linux&#xff0c;进行动调&#xff…

Android11修改连接WiFi后AP端显示的设备名

修改build.prop文件 1.修改 /system/build.prop 最后添加&#xff0c;xxx 为自己设置的设备名&#xff1a; net.hostnamexxx 2. 重启、重连wifi&#xff0c;从热点或路由器后台查看设备名即为修改后的名称 代码里动态配置 暴力手段&#xff1a;grep -rn “net.hostname” *…

matlab中的iddata函数的初步理解和使用程序举例

matlab中的iddata函数的初步理解和程序举例 一、iddata函数功能 iddata函数常用于系统识别分析领域数据分析方面。该函数在时域或频域中&#xff0c;将用于系统识别的输入输出数据及其特性数据的生成对象数据类型。即&#xff0c;可以使用iddata函数封装要标识的系统的输入和…

【Transformer从零开始代码实现】(一)输入部件:embedding+positionalEncoding

Transformer总架构图 输入相关组件 输入部分&#xff1a; 源文本嵌入层位置编码器目标文本嵌入层位置编码器 &#xff08;1&#xff09;Embedding 首先&#xff0c;需要对输入的内容进行向量化。 1&#xff09;先导示例 nn.Embedding示例&#xff1a; # 10代表嵌入的数…

即插即用篇 | YOLOv8 引入反向残差注意力模块 iRMB | 《ICCV 2023 最新论文》

论文地址:https://arxiv.org/abs/2301.01146 代码地址:https://github.com/zhangzjn/EMO 本论文着重于开发现代、高效、轻量级的模型,用于进行密集预测,同时在参数、FLOPs和性能之间进行权衡。倒置残差块(IRB)作为轻量级CNN的基础设施,但在基于注意力的研究中尚未找到对…