C++:类和对象 III(初始化列表、explicit、友元、匿名对象)

目录

初始化列表

初始化列表的特点

类型转换、explicit

隐式类型转换

explicit关键字

static成员

静态成员变量

静态成员函数

友元

友元函数

友元类

内部类

匿名对象

编译器优化


初始化列表

初始化列表就是类成员初始化的地方

函数有它声明和定义的地方,变量也有,类成员也有

先来看看日期类的默认构造函数

class Date
{
public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;
};int main()
{Date d1(2024, 7, 15);return 0;
}

在类里面显示出来的成员我们把它叫做声明

如果你认为我们就这样把_year,_month,_day给初始化了,那就错了

这并不是初始化,这是赋值

如果我们要初始化类成员的话应该这样做

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

 

这一块就是初始化列表

先是一个冒号开始,然后以逗号分隔,成员变量后面跟初始值或者表达式

这样我们就完成了一个成员的定义和初始化

那么这两种写法有什么区别呢?

这两种写法给我们带来的效果都是一样的,但是无论怎么写我们的成员变量都要经过初始化列表一遍,就算没有写初始化列表也会!所以这里建议尽量使用初始化列表初始化

如果我们不知道初始化列表怎么给,我们可以在声明的地方给一个缺省值,例如:

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

这样就算我们没有写初始化列表也会给我们自动初始化上这些值

注意:

const成员变量,带引用的成员变量,只有一次初始化的机会!那就是在初始化列表中!

class A
{
public:A(int a = 1){_a = a;}
private:const int _a;
};

因为const常量不能被赋值,只能在初始化的地方被初始化,正确代码如下: 

class A
{
public:A(int a = 1):_a(a){}
private:const int _a;
};
class A
{
public:A(int a = 1):_a(a){}
private:int& _a;
};

初始化列表的特点

如果我们没有在声明的地方给缺省值,也没有写初始化列表,那么值由编译器决定

如果我们给了缺省值没有写初始化列表,那么会根据缺省值初始化

如果我们即给了缺省值也给了初始化列表,那么根据初始化列表的值初始化

注:上述行为于构造函数内部行为无关

初始化列表中按照成员在类中声明的定义来初始化,于初始化列表中出现的先后顺序无关

例如:

class A
{
public:A():_a(1), _b(_a){}
//private:int _b;int _a;
};int main()
{A a;cout << a._a << endl;cout << a._b << endl;return 0;
}

输出结果:

我们可以看到_a的值为1,_b的值为随机值s

这是因为类声明是先声明的_b,才声明的_a,所以初始化列表会先初始化_b,再初始化_a,而不是因为_a在初始化列表中初始化就初始化_a

类型转换、explicit

隐式类型转换

什么是隐式类型转换?通过下面的例子我想你就明白了

int main()
{double d = 3.14;int i = d;cout << i << endl;return 0;
}

double类型的d为什么能赋值给int类型的i?

在 i = d 的时候这里会构造出一个临时对象,d会先构造给这个临时变量,然后临时变量才会将值拷贝构造给i

回到正题

class A
{
public:A(int a):_a(a){}
private:int _a;
};int main()
{A a = 1;return 0;
}

这里将1拷贝构造给A类型a就是隐式类型转换,将int类型转换成A类型

explicit关键字

如果我们想要编译器再严格一点,只能同类型转换,那么我们可以在构造类型前面加上关键字explicit

class A
{
public:explicit A(int a):_a(a){}
private:int _a;
};int main()
{A a = 1;return 0;
}

此时我们就运行不了我们的代码了

static成员

静态成员变量

用static修饰的成员变量,称为静态成员变量,静态成员变量必须要在类外初始化!

它的作用和C语言中一样,不属于某个具体的对象,存放在静态区中,生命周期跟全局变量一致

类内初始化:

class A
{
public:A(int a):_a(a){}void Print(){cout << _a << endl;}
private:static int _a;
};int main()
{A a;a.Print();return 0;
}

类外初始化:

class A
{
public://A(int a)//	:_a(a)//{}void Print(){cout << _a << endl;}
private:static int _a;
};int A::_a = 5;int main()
{A a;a.Print();return 0;
}

静态成员函数也收public、private、protected访问限定符约束

class A
{
public://A(int a = 1)//	:_a(a)//{}void Print(){cout << _a << endl;}
private:static int _a;
};int A::_a = 5;int main()
{A a;cout << A::_a << endl;return 0;
}

静态成员函数

用static修饰的成员函数,称之为静态成员函数,静态成员函数没有this指针!

静态成员函数中可以访问静态成员,但是不能访问成员函数,因为没有this指针

class A
{
public:A(int a = 1):_a(a){}static void Print(){cout << _a << endl;}
private:int _a;
};

非静态成员函数可以访问任意的静态成员变量和静态成员函数(毕竟是全局的)

友元

友元分为友元函数友元类

友元函数

class A
{
public:A(int a = 1):_a(a){}
private:int _a;
};void Print(const A& a)
{cout << a._a << endl;
}

正常这样Print函数是无法访问到A类里面的private成员变量的,但是如果它是A类的朋友就可以了

class A
{friend void Print(const A& a);public:A(int a = 1):_a(a){}
private:int _a;
};void Print(const A& a)
{cout << a._a << endl;
}

友元函数仅仅是一种声明,他不是类的成员函数

把函数的第一行写一遍,然后前面加上friend,这样Print就是A的朋友了,这样就不会报错了

可以看出,外部友元函数是可以访问类中的私有成员的,当然保护和共有也不例外

友元类

class A
{
public:A(int a = 1):_a(a){}void Print(){B b;cout << b._b << endl;}
private:int _a;
};class B
{friend class A;
public:B(int b = 0):_b(b){}
private:int _b;
};

正常来说A类中的Print函数是不能访问b中private的_b

但是这时候,B声明了A是我的朋友,那么A中就可以访问B类中的私有了

但是要注意:友元类关系不能传递

B说A是我的朋友,但是不代表A的朋友是B

class A
{
public:A(int a = 1):_a(a){}void Print(){B b;cout << b._b << endl;}
private:int _a;
};class B
{friend class A;
public:B(int b = 0):_b(b){}void Print(){A a;cout << a._a << endl;}
private:int _b;
};

A是B的友元,但是B可不是A的友元! 

友元和面向对象封装的思想有些相反了,破坏了这种封装

友元会增加耦合度,但是破坏了封装,所以不宜多用

内部类

如果一个类定义在另一个类的内部,那么这个类叫做内部类

class A
{
public:A(int a = 1):_a(a){}class B{public:void func(const A& a){cout << a._a << endl;}};
private:int _a;
};int main()
{A a;A::B b;b.func(a);return 0;
}

这里的B类就是定义在A类中,此时B就是A的友元,可以访问A类中的私有 

A类的实现就是专门为B准备的

匿名对象

先来看个代码

class A {
public:int func(int n) {//...return n;}
};int main()
{A a;a.func(1);return 0;
}

如果要调用func函数我们可以先定义一个A类对象,然后再使用func

那还有什么更好的调用方式吗?

我们可以使用匿名对象来调用这个func函数

class A {
public:int func(int n) {//...return n;}
};int main()
{A().func(1);return 0;
}

这里的A()就是一个匿名对象,它的生命周期只有这一行,这一行之后就会结束,所以目的也就只是想调用func函数不那么麻烦且少一点空间

编译器优化

class A {
public:A(int a = 1):_a(a){cout << "A(int a)" << endl;}A(const A& a):_a(a._a){cout << "A(const A& a)" << endl;}~A(){cout << "~A()" << endl;}
private:int _a;
};int main()
{A a1(10);A a2 = a1;return 0;
}

明明有两个成员变量a1、a2,怎么才调用一次构造函数呢?

这里就是由于编译器的优化,由于A a2 = a1这个表达式做了两件事情,一件就是构造,另一件就是拷贝构造,所以编译器优化成了只有拷贝构造即可

越是新版本的编译器优化的会越狠

将Debug模式换成Release也会有更多的优化

这些都是为了加快代码运行的效率


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

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

相关文章

c# .net core中间件,生命周期

某些模块和处理程序具有存储在 Web.config 中的配置选项。但是在 ASP.NET Core 中&#xff0c;使用新配置模型取代了 Web.config。 HTTP 模块和处理程序如何工作 官网地址&#xff1a; 将 HTTP 处理程序和模块迁移到 ASP.NET Core 中间件 | Microsoft Learn 处理程序是&#xf…

Java跨平台的原理是什么?JDK,JRE,JVM三者的作用和区别?xxx.java和xxx.class有什么区别?看这一篇就够了

目录 1. Java跨平台相关问题 1.1 什么是跨平台(平台无关性)&#xff1f; 1.2 跨平台(平台无关性)的好处&#xff1f; 1.3 编译原理基础&#xff08;Java程序编译过程&#xff09; 1.4Java跨平台的是实现原理&#xff1f; 1.4.1 JVM(Java虚拟机) 1.4.2 Class文件 1.4.3 …

Pytorch基础应用

1.数据加载 1.1 读取文本文件 方法一&#xff1a;使用 open() 函数和 read() 方法 # 打开文件并读取全部内容 file_path example.txt # 替换为你的文件路径 with open(file_path, r) as file:content file.read()print(content)方法二&#xff1a;逐行读取文件内容 # 逐…

Spring框架之DI依赖注入

Di(Dependence Injection)依赖注入,在spring框架负责创建bean对象时,动态地将依赖对象注入到其它对象中 一、什么是依赖注入。 我们在下面构建spring的过程中体会依赖注入&#xff1b; 从上面的图中我们知道&#xff0c;在ssm框架中服务层&#xff08;server&#xff09;无法直…

(三)C++之运算符重载

一.概念 C准许以运算符命名函数&#xff01;&#xff01;&#xff01; string a “hello”; a “ world”;// (a, “world”); cout<<“hello”; // <<(cout, “hello”); 可重载的运算符 不可重载的运算符 二.成员函数式(第一个行参是对象的引用) class T…

如何在AWS上构建Apache DolphinScheduler

引言 随着云计算技术的发展&#xff0c;Amazon Web Services (AWS) 作为一个开放的平台&#xff0c;一直在帮助开发者更好的在云上构建和使用开源软件&#xff0c;同时也与开源社区紧密合作&#xff0c;推动开源项目的发展。 本文主要探讨2024年值得关注的一些开源软件及其在…

前端vue3 实现pdf 生成的 类插件

前端 PDF 打印插件&#xff0c;基于 jspdf 和 html2canvas 开发 jspdfhtml2canvas 安装依赖 jspdf、html2canvas npm i jspdf html2canvas直接上代码 hooks

谷粒商城-全文检索-ElasticSearch

1.简介 一个分布式的开源搜索和分析引擎,可以 秒 级的从海量数据中检索 主要功能:做数据的检索和分析(MySQL专攻于数据的持久化存储与管理CRUD达到百万以上的数据MSQL就会很慢,海量数据的检索和分析还是要用ElasticSearch) 用途:我们电商项目里的所有的检索功能都是由Elasti…

【机器学习】--过采样原理及代码详解

过采样&#xff08;Oversampling&#xff09;是一个在多个领域都有应用的技术&#xff0c;其具体含义和应用方法会根据领域的不同而有所差异。以下是对过采样技术的详细解析&#xff0c;主要从机器学习和信号处理两个领域进行阐述。 一、机器学习中的过采样 在机器学习中&…

【BUG】已解决:note: This is an issue with the package mentioned above,not pip.

已解决&#xff1a;note: This is an issue with the package mentioned above&#xff0c;not pip. 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博主英杰&#xff0c;211科班出身&#xff0c;就职于医疗科技公司&#xff0c;热衷…

园区AR导航系统构建详解:从三维地图构建到AR融合导航的实现

随着现代园区规模的不断扩大与功能的日益复杂&#xff0c;传统的二维地图导航已难以满足访客高效、精准定位的需求。园区内部错综复杂的布局、频繁变更的商户位置常常让访客感到迷茫&#xff0c;造成寻路上的时间浪费。园区AR导航系统以创新的技术手段&#xff0c;破解了私域地…

签名优化:请求数据类型不是`application/json`,将只对随机数进行签名计算,例如文件上传接口。

文章目录 I 签名进行请求数据类型类型判断1.1 常见的ContentType1.2 签名切面处理1.3 文件上传案例1.4 处理接口信息背景: 文件上传接口的请求数据类型通常为multipart/form-data,方便携带文本域和使用接口文档进行调试。 如果携带JSON数据,不方便调试接口。 前端数据也要特…

网络安全-等级保护制度介绍

一、等保发展历程 &#xff08;1&#xff09;1994国务院147号令 第一次提出等级保护概念&#xff0c;要求对信息系统分等级进行保护 &#xff08;2&#xff09;1999年GB17859 国家强制标准发布&#xff0c;信息系统等级保护必须遵循的法规 &#xff08;3&#xff09;2005年公安…

JavaWeb笔记_Response对象

一.Response对象 1.1 Response对象概述 a.专门负责给浏览器响应信息&#xff08;响应行&#xff0c;响应头&#xff0c;响应体&#xff09;的对象 b.我们主要使用的是跟HTTP协议相关的Response对象&#xff1a;HTTPServletResponse&#xff0c;继承了ServletResponse&#x…

Spring Boot集成syslog快速入门Demo

1.什么syslog&#xff1f; Syslog-ng是由Balabit IT Security Ltd.维护的一套开源的Unix和类Unix系统的日志服务套件。它是一个灵活的、可伸缩的系统日志记录程序。对于服务器日志集中收集&#xff0c;使用它是一个不错的解决方案。syslog-ng (syslog-Next generation) 是sysl…

模型训练中出现loss为NaN怎么办?

文章目录 一、模型训练中出现loss为NaN原因1. 学习率过高2. 梯度消失或爆炸3. 数据不平衡或异常4. 模型不稳定5. 过拟合 二、 针对梯度消失或爆炸的解决方案1. 使用torch.autograd.detect_anomaly()2. 使用 torchviz 可视化计算图3. 检查梯度的数值范围4. 调整梯度剪裁 三、更具…

C++树(二)【直径,中心】

目录&#xff1a; 树的直径&#xff1a; 树的直径的性质&#xff1a; 性质1&#xff1a;直径的端点一定是叶子节点 性质2&#xff1a;任意点的最长链端点一定是直径端点。 性质3&#xff1a;如果一棵树有多条直径,那么它们必然相交&#xff0c;且有极长连…

自定义注解 + Redis 实现业务的幂等性

1.实现幂等性思路 实现幂等性有两种方式&#xff1a; ⭐ 1. 在数据库层面进行幂等性处理&#xff08;数据库添加唯一约束&#xff09;. 例如&#xff1a;新增用户幂等性处理&#xff0c;username 字段可以添加唯一约束. ⭐ 2. 在应用程序层面进行幂等性处理. 而在应用程序…

一款由AI编写,简洁而实用的开源IP信息查看器

大家好&#xff0c;今天给大家分享一款用于查询和显示用户当前 IP 地址的轻量级项目MyIP。 MyIP提供了多种功能&#xff0c;包括IP地址查询、网络连通性检查、WebRTC连接检测、DNS泄露检查、网速测试、MTR测试等等。 使用MyIP&#xff0c;我们可以轻松地查看自己的公网IP地址&…

Linux网络——套接字与UdpServer

目录 一、socket 编程接口 1.1 sockaddr 结构 1.2 socket 常见API 二、封装 InetAddr 三、网络字节序 四、封装通用 UdpServer 服务端 4.1 整体框架 4.2 类的初始化 4.2.1 socket 4.2.2 bind 4.2.3 创建流式套接字 4.2.4 填充结构体 4.3 服务器的运行 4.3.1 rec…