类和对象(下篇)
再谈构造函数
构造函数体赋值
在之前的学习中我们知道,在创建一个对象时,我们的编译器就会自动调用构造函数将对象初始化,给对象中各个成员变量一个合适的初始值。
例如:
class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;
};
虽然上述构造函数调用之后,对象中已经有了一个初始值,但是不能将其称为对对象中成员变量的初始化,构造函数体中的语句只能将其称为赋初值,而不能称作初始化。因为初始化只能初始化一次,而构造函数体内可以多次赋值。
所以C++就引入了初始化列表的概念:
初始化列表
初始化列表的形式如下:
以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式。
废话不多说,直接上代码:
成员变量括号后面就放入合适的初始值
class Date
{
public:Date(int year, int month, int day): _year(year), _month(month), _day(day){}
private:int _year;int _month;int _day;
};
这里需要注意几个点:
- 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
- 类中包含以下成员,必须放在初始化列表位置进行初始化:
1 引用成员变量
2 const成员变量
3 自定义类型成员(且该类没有默认构造函数时)
直接上代码:
可以看到,这段代码中A类中并没有无参或者全缺省的构造函数,所以没有默认构造函数,必须使用列表初始化
而int& ref则是引用变量
_a 的类型时const int
他们都必须使用列表初始化
所以这段代码中的A类和B类都是使用列表初始化
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
};
- 尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化。(所以在之后的代码中我们初始化时可以尽量地去使用列表初始化)
- 成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关
下面看一段代码:
大家可以来思考一下结果
class A
{
public:A(int a):_a1(a), _a2(_a1){}void Print() {cout << _a1 << " " << _a2 << endl;}
private:int _a2;int _a1;
};
int main()
{A aa(1);aa.Print();
}
答案是a1的输出值是1,a2的输出值时随机值
因为a2比a1 先声明,所以在列表初始化时先初始化a2,而此时a1也是未知的随机值,所以a2就被初始化为随机值,然后a1初始化为1
explicit关键字
构造函数不仅可以构造与初始化对象,对于单个参数或者除第一个参数无默认值其余均有默认值的构造函数,还具有类型转换的作用。
例如:
这段代码中我们直接用=赋值初始化,但是他实质是是一个类型转换
其实test函数里面的两句代码效果是一样的,但是第二句的类型转化会产生一个临时变量,调用构造函数初始化,然后在调用拷贝构造函数,但是我们的编译器将他直接优化成了直接调用构造函数
class Date
{
public:Date(int year):_year(year){}Date& operator=(const Date& d){if (this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;}
private:int _year;int _month;int _day;
};
void Test()
{
//这两句的用法都可,效果是一样的Date d1(2022);d1 = 2023;
}
下面看这段代码:
你就会发现编译错误
这就引出了重点:
用explicit修饰构造函数,将会禁止构造函数的隐式转换
class Date
{
public:explicit Date(int year):_year(year){}explicit Date(int year, int month = 1, int day = 1): _year(year), _month(month), _day(day){}Date& operator=(const Date& d){if (this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;}
private:int _year;int _month;int _day;
};
void Test()
{Date d1(2022);d1 = 2023;
}
static成员
概念
声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的成员函数,称之为静态成员函数。
这里要注意:
静态成员变量一定要在类外进行初始化
还有:
静态成员函数和变量不属于某个对象,而是属于整个类
例如:
这种初始化方式就会发生编译错误
class Date
{static int _year=0;
};int main()
{Date d1;
}
应该这样初始化:
class Date
{static int _year;
};
static int _year = 0;
int main()
{Date d1;
}
特性
- 静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区
- 静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明
- 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问
- 静态成员函数没有隐藏的this指针,不能访问任何非静态成员
- 静态成员也是类的成员,受public、protected、private 访问限定符的限制
下面请看一个经典的面试题:
实现一个类,计算程序中创建出了多少个类对象。
这里我们就需要来解释一下,我们讲解放入注释
class A
{
public:A() //这是一个构造函数,创建一个对象就会调用一次,所以_scount++{ ++_scount;}A(const A& t) //构造函数的重载{ ++_scount; }~A() //析构函数,清理一个对象_scount就--{ --_scount; }static int GetACount() //静态成员函数{ //只能访问静态成员变量return _scount; //最后返回这个静态成员变量即可}
private:static int _scount; //静态成员变量的声明
};
int A::_scount = 0; //需要访问私有成员变量,所以要用类名,并且在类外初始化
void TestA()
{cout << A::GetACount() << endl;A a1, a2;A a3(a1);cout << A::GetACount() << endl;
}
可以看到,输出结果如下,刚开始没有创建对象输出0
创建a1 a2 a3 后输出3
友元
友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。
而友元一共分为两类:
友元函数和友元类
友元函数
在之前的运算符重载中我们就可以用友元来结局全局函数不可以调用私有成员变量的问题:
友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字。
例如:
class Date
{friend ostream& operator<<(ostream& _cout, const Date& d);friend istream& operator>>(istream& _cin, Date& d);
public:Date(int year = 1900, int month = 1, int day = 1): _year(year), _month(month), _day(day){}
private:int _year;int _month;int _day;
};
ostream& operator<<(ostream& _cout, const Date& d)
{_cout << d._year << "-" << d._month << "-" << d._day;return _cout;
}
istream& operator>>(istream& _cin, Date& d)
{_cin >> d._year;_cin >> d._month;_cin >> d._day;return _cin;
}
int main()
{Date d;cin >> d;cout << d << endl;return 0;
}
这里还需要注意几个点:
友元函数可访问类的私有和保护成员,但不是类的成员函数
友元函数不能用const修饰
友元函数可以在类定义的任何地方声明,不受类访问限定符限制
一个函数可以是多个类的友元函数
友元函数的调用与普通函数的调用原理相同
友元类
友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
请看代码:
class Time
{friend class Date; // 声明日期类为时间类的友元类,则在日期类中就直接访问Time类中的私有成员变量
public:Time(int hour = 0, int minute = 0, int second = 0): _hour(hour), _minute(minute), _second(second){}private:int _hour;int _minute;int _second;
};
class Date
{
public:Date(int year = 1900, int month = 1, int day = 1): _year(year), _month(month), _day(day){}void SetTimeOfDate(int hour, int minute, int second){// 直接访问时间类私有的成员变量_t._hour = hour;_t._minute = minute;_t._second = second;}private:int _year;int _month;int _day;Time _t;
};
下面请看友元类的特点:
1 友元关系是单向的,不具有交换性。
比如上述Time类和Date类,在Time类中声明Date类为其友元类,
那么可以在Date类中直接
2 访问Time类的私有成员变量,
但想在Time类中访问Date类中私有的成员变量则不行。
友元关系不能传递
3 如果C是B的友元, B是A的友元,则不能说明C时A的友元。
友元关系不能继承,在继承位置再给大家详细介绍。
内部类
概念:如果一个类定义在另一个类的内部,这个内部类就叫做内部类。
内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越的访问权限。
这里需要注意:
内部类就是外部类的友元类,所以内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元。
特性:
- 内部类可以定义在外部类的public、protected、private都是可以的。
- 注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名。
- sizeof(外部类)=外部类,和内部类没有任何关系。
用代码看看就懂了
class A
{
private:static int k;int h;
public:class B // B天生就是A的友元{public:void foo(const A& a){//B是A的友元,所以可以访问A的非公有成员变量cout << k << endl;cout << a.h << endl;}};
};
int A::k = 1;
int main()
{A::B b;b.foo(A());return 0;
}
匿名对象
我们要注意:
匿名对象的生命周期只有一行:
请看代码:
class A
{
public:void print(){cout << _a << endl;}~A(){cout << "~A()" << endl;}
private:int _a;
};int main()
{A().print();
}
输出结果可以看到这一行就直接调用了析构函数清理自己,所以他的生命周期只有一行
匿名对象的使用有时候可以起到简化代码和节省空间的作用
不做过多的讲解,大家看代码的里面的注释即可
class A
{
public:A(int a = 0):_a(a){cout << "A(int a)" << endl;}~A(){cout << "~A()" << endl;}
private:int _a;
};
class Solution {
public:int Sum_Solution(int n) {//...return n;}
};
int main()
{A aa1;// 不能这么定义对象,因为编译器无法识别下面是一个函数声明,还是对象定义//A aa1();// 但是我们可以这么定义匿名对象,匿名对象的特点不用取名字,// 但是他的生命周期只有这一行,我们可以看到下一行他就会自动调用析构函数A();A aa2(2);// 匿名对象在这样场景下就很好用,当然还有一些其他使用场景,这个我们以后遇到了再说Solution().Sum_Solution(10);return 0;
}
好了,今天的分享到这里就结束了,谢谢大家的支持!
相关文章:
类和对象(下篇)
再谈构造函数 构造函数体赋值 在之前的学习中我们知道,在创建一个对象时,我们的编译器就会自动调用构造函数将对象初始化,给对象中各个成员变量一个合适的初始值。 例如: class Date { public:Date(int year, int month, int d…...
华为鸿蒙(HarmonyOS):连接一切,智慧无限
华为鸿蒙是一款全场景、分布式操作系统,旨在构建一个真正统一的硬件生态系统。该操作系统于2019年8月首次发布,并被设计为可以应用于各种设备,包括智能手机、智能手表、智能电视、车载系统等多种智能设备。 推荐一套最新版的鸿蒙4.0开发教程 …...
劈窗算法反演地表温度
目录 摘要操作步骤提取热红外单波段提取NDVI同步像元分辨率与个数劈窗算法地表温度反演制图 摘要 主要使用HJ-2(环境减灾二号卫星)的IRS传感器的两个热红外波段,以及红波段与近红波段计算得到的NDVI,使用劈窗算法,得到…...
持续集成交付CICD:基于ArgoCD 的GitOps 自动化完成前端项目应用发布与回滚
目录 一、实验 1. 环境 2. K8S master节点部署Argo CD 3.基于ArgoCD 实现GitOps (同步部署文件) 4.基于ArgoCD 实现GitOps (同步HELM文件) 二、问题 1. ArgoCD 连接K8S集群状态为 Unknown 2.ArgoCD 创建application失败 …...
SSH无密登陆配置
1 SSH介绍 ssh命令用于远程登录到其他计算机,实现安全的远程管理。 基本语法: ssh 域名/IP地址 示例: (1)从hadoop100服务器上远程连接hadoop101服务器 [hadoophadoop100 ~]$ ssh hadoop101 如果出现如下内容 Ar…...
【bug日记】如何切换jdk版本,如何解决java和javac版本不一致
背景 今天在安装jenkins后,使用java运行war包的时候,提示jdk1.8版本太低,需要提高版本,所以就需要切换jdk版本 解决 在用户变量中,首先更改了JAVA_HOME的地址为17的目录,发现javac的版本改为17了&#x…...
【C语言】6-5 判断回文字符串 分数 20
6-5 判断回文字符串 分数 20 全屏浏览题目 切换布局 作者 C课程组 单位 浙江大学 本题要求编写函数,判断给定的一串字符是否为“回文”。所谓“回文”是指顺读和倒读都一样的字符串。如“XYZYX”和“xyzzyx”都是回文。 函数接口定义: bool palindr…...
STL中优先队列(堆)的详解
文章目录 priority_queue的基本介绍堆(heap)堆的概念与结构 priority_queue 的介绍与使用 priority_queue的基本介绍 这个priority_queue翻译成中文就是优先级队列,但其实我们很难去一眼看出他的意思到底是什么,他的逻辑结构实际上类似于数据结构中的堆…...
@vue/cli脚手架
0_vue/cli 脚手架介绍 目标: webpack自己配置环境很麻烦, 下载vue/cli包,用vue命令创建脚手架项目 vue/cli是Vue官方提供的一个全局模块包(得到vue命令), 此包用于创建脚手架项目 脚手架是为了保证各施工过程顺利进行而搭设的工作平 vue/cli的好处 开箱即用 0配置webpack babe…...
在 MyBatis 中<应该怎么写
在 MyBatis 中,< 符号在 XML 配置文件中是一个特殊字符,用于标记 XML 标签的开始。因此,如果你在 MyBatis 的 if 标签中直接使用 < 符号,它会被解析为 XML 标签的开始,从而导致解析错误。 为了避免这个问题&…...
采访亚马逊云科技代闻:深度解读2023re:Invent与生成式AI
2023亚马逊云科技re:Invent已于拉斯维加斯圆满落幕,为进一步解析re:Invent 2023能够对开发者带来哪些深刻影响,亚马逊云科技大中华区解决方案架构部总经理代闻在大会现场接受了InfoQ中国创始人霍太稳的采访,并就re:Invent 2023的前沿洞察与重…...
黑豹程序员-安装docker-ce
docker分为商用版和社区版,我们使用社区版CE 1 安装yum-utils包(提供yum-config-manager 实用程序)并设置阿里镜像库 sudo yum install -y yum-utils sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/cent…...
多臂老虎机算法步骤
内容导航 类别内容导航机器学习机器学习算法应用场景与评价指标机器学习算法—分类机器学习算法—回归机器学习算法—聚类机器学习算法—异常检测机器学习算法—时间序列数据可视化数据可视化—折线图数据可视化—箱线图数据可视化—柱状图数据可视化—饼图、环形图、雷达图统…...
pgsql的jsonb相关处理及样例
目录 1、某个字段中包含目标list中的全部使用>: 2、某个字段中包含目标list中任意值使用?|: 3、其他操作样例: 1、某个字段中包含目标list中的全部使用>: SELECT * FROM "public"."t_a" WHERE a::j…...
LeetCode-17 电话号码的字母组合
LeetCode-17 电话号码的字母组合 给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。 示例 1: 输入:d…...
Ubuntu 22.04 系统创建用户并授权sudo权限
文章目录 Ubuntu 22.04 系统创建用户并授权sudo权限添加用户将用户添加到 sudo 用户组中,以使其具有执行需要管理员权限的命令的能力 Ubuntu 22.04 系统创建用户并授权sudo权限 添加用户 adduser zkdocker我们刚刚创建了一个名为“zkdocker”的新用户,…...
Vue2源码梳理:源码构建流程与运行时和编译时的版本选择
Vue.js 源码构建 1 )rollup 和 webpack 的对比 vuejs的源码呢是基于rollup构建的 参考: https://github.com/rollup/rollup rollup 和 webpack 都是一个构建工具 webpack 它会更强大一些, 会把像图片, css等静态资源通通编译成javascriptrollup 更适合一种javscri…...
透视数据:数据可视化工具的多重场景应用
数据可视化工具已经成为了许多领域中的重要利器,它们在各种场景下发挥着重要作用。下面我就以可视化从业者的角度简单谈谈数据可视化工具在不同场景下的应用: 企业数据分析与决策支持 在企业层面,数据可视化工具被广泛应用于数据分析和决策…...
系列十四(面试)、谈谈你对StackOverflowError的理解?
一、StackOverflowError 1.1、概述 StackOverflowError是栈内存溢出的意思。栈中主要存储的是8种基本数据类型 引用类型 实例方法,栈的空间也是有限的,当存储进栈中的容量大于栈的最大容量时,就会报StackOverflowError的错误。 1.2、案例 …...
【WebRTC---源码篇】(二十五)音视频同步
RTC音视频同步场景: 音视频不在同一个时间点开始采集,如在视频先采集,音频后采集的情况下。我们不能贸然的认为音频起点来对齐视频起点,这种情况下,如何对音视频进行处理,就涉及到了音视频同步的知识。 解决思路: 通过现有条件,我们拥有RTP和SR,那么是不是可以用这两…...
鸿蒙开发之统一样式, @Styles 复用样式
只能使用通用样式 Entry Component struct Test {// 样式 就近原则 即{}之内的优先级更高 Styles customStyles(){.width(200).height(60).backgroundColor(Color.Red)}build() {Row() {Column({ space: 5 }) {Text("自定义样式").fontSize(30).textAlign(TextAlign…...
解决java内存问题
遇到 Java 控制台程序中的 Exception in thread “main” java.lang.OutOfMemoryError: Java heap space 错误通常意味着程序在其分配的堆内存空间中耗尽了内存。这个问题通常可以通过以下方法解决: 增加堆内存大小 可以通过调整 JVM(Java虚拟机&#x…...
分享5款为你生活带来便捷的小工具
生活需要一些小巧而贴心的工具,它们能够在细节处为我们带来便捷。这五款工具简洁而实用,看看它们是否适合融入你的生活。 1.图片压缩——TinyPNG TinyPNG是一款图片压缩工具,可以智能地减少WebP、PNG和JPEG图片的文件大小。TinyPNG通…...
【Java JVM】JVM 分析工具
在 $JAVA_HOME/bin 的目录下, 存在着许多小工具, 除了编译和运行 Java 程序外, 打包, 部署, 签名, 调试, 监控, 运维等各种场景都可能会用到它们。 1 常用的命令行工具 1.1 jps (JVM Process Status Tool) - 虚拟机进程状况工具 列出正在运行的虚拟机进程, 并显示虚拟机执行…...
融资项目——vue之双向数据绑定
上一篇文章中使用的v-bind是单向绑定方法,即数据改变,网页相应的视图发生改变,但是网页视图发生改变其相关联的数据不会发生改变。但是双向数据绑定不同之处在于网页视图发生改变其相关联的数据也会发生改变。Vue可以使用v-model进行双向数据…...
『番外篇五』SwiftUI 进阶之如何动态获取任意视图的 tag 和 id 值
概览 在某些场景下,我们需要用代码动态去探查 SwiftUI 视图的信息。比如任意视图的 id 或 tag 值: 如上图所示:我们通过动态探查技术在运行时将 SwiftUI 特定视图的 tag 和 id 值显示在了屏幕上。 这是如何做到的呢? 在本篇博文,您将学到如下内容: 概览1. “如意如意,…...
姿态识别、目标检测和跟踪的综合应用
引言: 近年来,随着人工智能技术的不断发展,姿态识别、目标检测和跟踪成为了计算机视觉领域的热门研究方向。这三个技术的综合应用为各个行业带来了巨大的变革和机遇。本文将分别介绍姿态识别、目标检测和跟踪的基本概念和算法,并探…...
数据结构考试测试编程题
作者前言 🎂 ✨✨✨✨✨✨🍧🍧🍧🍧🍧🍧🍧🎂 🎂 作者介绍: 🎂🎂 🎂 🎉🎉🎉…...
力扣每日一题day37[113.路径总和ii]
给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。 叶子节点 是指没有子节点的节点。 示例 1: 输入:root [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum 22 输出&a…...
Keras使用sklearn中的交叉验证和网格搜索
Keras是Python在深度学习领域非常受欢迎的第三方库,但Keras的侧重点是深度学习,而不是所以的机器学习。事实上,Keras力求极简主义,只专注于快速、简单地定义和构建深度学习模型所需要的内容。Python中的scikit-learn是非常受欢迎的…...
网站的动态新闻数据库怎么做/阿里巴巴关键词排名优化
环中最长子串 题目 给你一个字符串s,首尾相连成一个环形, 请你在环中找出o字符出现了偶数次最长子字符串的长度. 备注: 1 <= s.lenth <= 5x10^5 s只包含小写英文字母 输入 输入是一个小写字母组成的字符串 输出描述 输出是一个整数 示例一 输入 alolobo输出 6说…...
行业网站作用/营销是做什么
docker的网络Docker 安装时会自动在host上创建三个网络,我们可用 docker network ls命令查看:[rootlocalhost ~]# docker network lsNETWORK ID NAME DRIVER SCOPE0164da7ee66a bridge bridge…...
郑州响应式网站建设/百度小说风云榜
现在python学习可是潮流,相信很多小伙伴目前都在学习或者正准备学习python的路上。虽然说python语言相对比较简单,上手很容易,但是对于一些零基础的小白来说还是比较难的,在学习过程中会遇到各种各样的问题。小编今天跟大家分享的…...
营销型网站建设软件/免费网站推广群发软件
工作回顾前几天,我在公园里进行了一次冲刺回顾。 阳光普照,我们所有人都在努力完成积压工作,因此,这对每个人的努力都是一种不错的回报。 在户外举行回顾展也可以使它充满活力和热情,而这在小房间里很难找到。 我以前…...
专业烟台房产网站建设/百度推广代理
通常word文件可以通过另存为html/htm的方式把图片从内容中拆分出来,网上一搜一大堆。 上述操作主要是针对.doc文件进行操作才能得到原图,现在我有.docx文件,使用这种方式就只能得到文档中被编辑过的图片,并且编辑过的图片拿出来会…...
自己怎么制作网址/seo点击排名软件哪里好
现在做一项手机评测,不跑个测试软件总是显得不科学。也有很多爱好者拿测试软件的跑分来作为手机性能的唯一依据。实际上,智能手机测试软件是个出现相当晚的东西,而智能手机测试软件因为各种原因,往往会存在一些问题。测出来的分数…...