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://xiahunao.cn/news/3249691.html

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

相关文章

xFormers - Transformers 加速研究的工具箱

文章目录 一、关于 xFormers特点 二、安装xFormers三、Benchmarks&#xff08;可选&#xff09;测试安装 四、使用xFormers1、Transformers 关键概念2、Repo map3、主要特点4、安装故障排除 一、关于 xFormers xFormers是一个基于PyTorch的库&#xff0c;其中包含灵活的Transf…

Java_MyBatisPlus

MyBatisPlus属于是MyBatis的拓展&#xff0c;不影响原MyBatis框架下的代码运行&#xff0c;并对MyBatis框架进行拓展及优化。 使用步骤&#xff1a; 注意&#xff1a;继承BaseMapper时要填写泛型为要操作的实体类。 基本原理&#xff1a; MyBatisPlus通过扫描实体类&#xff…

adb查看网卡信息,并修改网卡mac地址

这种方法修改mac后&#xff0c;关机后会失效! 文章结尾有永久修改mac地址的方法! 1. 查看网卡的信息&#xff0c;以及mac地址&#xff0c;ip地址&#xff0c;子网掩码等 //查看所有网卡信息adb shell ifconfig//MAC地址&#xff1a; HWaddr 5e:2c:e9:58:3e:4f //IP地址&a…

安全防御拓扑2

实验拓扑&#xff1a;​​​​​​​ 要求&#xff1a; 1、办公区设备可以通过电信链路和移动链路上网(多对多的NAT&#xff0c;并且需要保留一个公网IP不能用来转换) 2、分公司设备可以通过总公司的移动链路和电信链路访问到Dmz区的http服务器 3、多出口环境基于带宽比例进行…

Django定时任务框架django-apscheduler的使用

1.安装库 pip install django-apscheduler 2.添加 install_app django_apscheduler 3.在app下添加一个task.py文件&#xff0c;用来实现具体的定时任务 task.pydef my_scheduled_job():print("这个任务每3秒执行一次", time.time()) 4.在app下创建一个manag…

studio编译报错java.lang.NullPointerException

安卓studio编译报错&#xff0c;这个是一个新建的项目就报错&#xff0c;原因是 implementation androidx.appcompat:appcompat:1.7.0版本太高&#xff0c;修改后版本 implementation androidx.appcompat:appcompat:1.4.0&#xff0c; 编译又报错 18 issues were found wh…

【Node.js】初识 Node.js

Node.js 概念 Node.js 是一个开源与跨平台的 JavaScript运行时环境 &#xff0c;在浏览器外运行 V8 JavaScript 引擎(Google Chrome的内核)&#xff0c;利用事件驱动、非阻塞和异步输入输出 等技术提高性能。 可以理解为 Node.js就是一个服务器端的、非阻塞式 l/O 的、事件驱…

npm install时卡在sill idealTree buildDeps卡着不动

场景&#xff1a;做导出功能的时候要用上xlsx&#xff0c;正常npm install xlsx --save 问题描述&#xff1a;npm install时卡在sill idealTree buildDeps&#xff0c;&#xff0c;卡着不动 过程&#xff1a;在网上一顿百度试过好多种方法 1、切换taobao的镜像地址 npm conf…

Android 使用FFmpeg解析RTSP流,ANativeWindow渲染 使用SurfaceView播放流程详解

文章目录 ANativeWindow 介绍ANativeWindow 的主要功能和特点ANativeWindow 的常用函数工作流程原理图通过ANativeWindow渲染RGB纯色示例 播放RTSP流工作流程图关键步骤解析自定义SurfaceView组件native 层解码渲染 效果展示注意事项 这篇文章涉及到jni层&#xff0c;以及Ffmpe…

设计分享—国外网站设计赏析

今天还是给大家分享一些国外的网站设计案例&#xff5e; 蓝蓝设计是一家专注而深入的界面设计公司&#xff0c;为期望卓越的国内外企业提供卓越的大数据可视化界面设计、B端界面设计、桌面端界面设计、APP界面设计、图标定制、用户体验设计、交互设计、UI咨询、高端网站设计、平…

来参与“向日葵杯”全国教育仿真技术大赛~

可点击进行了解&#xff1a;“向日葵杯”全国教育仿真技术大赛 (sunmooc.cn) 本次大赛共分为四个赛道&#xff1a;自主命题赛道、教育知识图谱设计赛道、FPGA硬件扑克牌对抗赛道、EasyAR元宇宙空间设计赛道。 参赛对象 &#xff1a; 具有正式学籍的在校研究生&#xff0c;本科…

Memcached介绍与使用

引言 本文是笔者对Memcached这个高性能分布式缓存组件的实践案例&#xff0c;Memcached是一种高性能的分布式内存对象缓存系统&#xff0c;用于减轻数据库负载&#xff0c;加速动态Web应用&#xff0c;提高网站访问速度。它通过在内存中缓存数据和对象来减少读取数据库的次数&…

Postman导出excel文件

0 写在前面 在我们后端写接口的时候&#xff0c;前端页面还没有出来&#xff0c;我们就得先接口测试&#xff0c;在此记录下如何使用postman测试导出excel接口。 如果不会使用接口传参可以看我这篇博客如何使用Postman 1 方法一 2 方法二 3 写在末尾 虽然在代码中写入文件名…

NVIDIA Container Toolkit 安装与配置帮助文档(Ubuntu,Docker)

NVIDIA Container Toolkit 安装与配置帮助文档(Ubuntu,Docker) 本文档详细介绍了在 Ubuntu Server 22.04 上使用 Docker 安装和配置 NVIDIA Container Toolkit 的过程。 概述 NVIDIA 容器工具包使用户能够构建和运行 GPU 加速容器。即可以在容器中使用NVIDIA显卡。 架构图如…

uniapp H5 如何根据接口返回form表单,跳转银联支付界面?

uniapp如何根据form表单&#xff0c;唤醒第三方支付? 文章目录 uniapp如何根据form表单&#xff0c;唤醒第三方支付?效果图实现 效果图 接口返回 form 表单数据 实现 // 例请求成功&#xff0c;返回数据 rechargePay({}).then(res > {// 接收接口返回数据let { result …

= null 和 is null;SQL中关于NULL处理的4个陷阱;三值逻辑

一、概述 1、NULL参与的所有的比较和算术运算符(>,,<,<>,<,>,,-,*,/) 结果为unknown&#xff1b; 2、unknown的逻辑运算(AND、OR、NOT&#xff09;遵循三值运算的真值表&#xff1b; 3、如果运算结果直接返回用户&#xff0c;使用NULL来标识unknown 4、如…

JRT实体视图查询

JRT的设计目标就是多数据库支持&#xff0c;对于爬行周边数据提供DolerGet解决爬取多维数据问题。但是对于通过父表字段筛选子表数据就不能通过DolerGet取数据了&#xff0c;因为查询到的父表数据没有子表数据的ID。 比如下面表&#xff1a; 我需要按登记号查询这个登记号的报…

【linux】服务器安装NVIDIA驱动

【linux】服务器安装NVIDIA驱动 【创作不易&#xff0c;求点赞关注收藏】&#x1f600; 文章目录 【linux】服务器安装NVIDIA驱动一、关闭系统自带驱动nouveau二、下载英伟达驱动三、安装英伟达驱动1、禁用X服务器和相关进程2、在TTY终端安装驱动3、验证是否安装成功4、重新启…

接口开发:Orcal数据库的批量修改sql

场景&#xff1a;在日常的CURD中一定会用到批量修改。在我们的项目中&#xff0c;使用的数据库是Orcal&#xff0c;由于之前基本都是使用Mysql的&#xff0c;使用的sql语句也基本都是用mysql的。但是在这次的接口编写时用mysql的批量修改出了问题&#xff0c;刚开始我还以为是写…

源码分析SpringCloud Gateway如何加载断言(predicates)与过滤器(filters)

我们今天的主角是Gateway网关&#xff0c;一听名字就知道它基本的任务就是去分发路由。根据不同的指定名称去请求各个服务&#xff0c;下面是Gateway官方的解释&#xff1a; Spring Cloud Gateway&#xff0c;其他的博主就不多说了&#xff0c;大家多去官网看看&#xff0c;只…