类和对象(下篇)

再谈构造函数

构造函数体赋值

在之前的学习中我们知道,在创建一个对象时,我们的编译器就会自动调用构造函数将对象初始化,给对象中各个成员变量一个合适的初始值。
例如:

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. 类中包含以下成员,必须放在初始化列表位置进行初始化:
    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 
};
  1. 尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化。(所以在之后的代码中我们初始化时可以尽量地去使用列表初始化)
  2. 成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关

下面看一段代码:
大家可以来思考一下结果

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;
}
特性
  1. 静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区
  2. 静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明
  3. 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问
  4. 静态成员函数没有隐藏的this指针,不能访问任何非静态成员
  5. 静态成员也是类的成员,受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的友元。
友元关系不能继承,在继承位置再给大家详细介绍。

内部类

概念:如果一个类定义在另一个类的内部,这个内部类就叫做内部类。

内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越的访问权限。

这里需要注意:
内部类就是外部类的友元类,所以内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元。

特性:

  1. 内部类可以定义在外部类的public、protected、private都是可以的。
  2. 注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名。
  3. 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;
}

好了,今天的分享到这里就结束了,谢谢大家的支持!

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

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

相关文章

从零开始创建GPTs 人人都可以编写自己的ChatGPT产品

在这个人工智能迅猛发展的时代&#xff0c;GPT&#xff08;生成式预训练变换器&#xff09;已经成为一项令人兴奋的技术&#xff0c;它打开了创意和知识的新大门。无论你是一名编程新手、一位热爱探索的学生&#xff0c;还是对未来充满好奇的专业人士&#xff0c;GPTs都可以为你…

盘点2023年度安防监控行业发展:安防监控技术取得哪些进展?

随着科技的不断发展&#xff0c;安防监控行业也在迅速进步。在今年一年中&#xff0c;安防监控行业的发展也取得了长足的进步。随着2023年渐近尾声&#xff0c;本文将对2023年安防监控行业的发展进行一个盘点和简单的剖析。 1&#xff09;高清监控设备的发展越来越受到重视 随…

vue事件修饰符

在事件处理函数中调用event.preventDefault()或event.stopPropagation()是非常常见的需求。因此&#xff0c;vue提供了事件修饰符的概念&#xff0c;来辅助程序员更方便的对事件的触发就行控制&#xff0c;常用的5个事件修饰符如下&#xff1a; .prevent格式: .stop格式&#x…

Electron训练笔记

终端乱码解决办法&#xff1a;更改编号下载卡住解决办法&#xff1a;Electron RequestError: connect ETIMEDOUT 20.205.243.166:443electron本质是一个依赖库&#xff0c;改依赖库提供了部分对象&#xff0c;可以实现对于window的调用。electron有一个主进程&#xff0c;多个渲…

@XmlAccessorType+@XmlElement完美解决Java类到XML映射问题

前言&#xff1a; 最近项目在做静态代码扫描的时候&#xff0c;出现Java类中成员变量命名的问题&#xff0c;开头字母必须小写&#xff0c;但是这个类成员是对接其他公司的字段&#xff0c;对方提供的请求格式是XML&#xff0c;必须将Java类转化为XML的格式&#xff0c;而且这…

Qlistwidget双击编辑的时候 如何控制编辑框大小

1 默认的时候比较编辑框比较小&#xff0c;当设置 ListWidget->editItem(item);的时候会出现一个比较lineedit的&#xff0c;通过设置qss&#xff0c;可以控制lineedit的宽度&#xff0c;但是高度控制不了。通过qss设置lineedit border: 0px&#xff1b;勉强可以看到item的…

Debezium系列之:Flink SQL消费Debezium数据格式,同步数据到下游存储系统

Debezium系列之:Flink SQL消费Debezium数据格式,同步数据到下游存储系统 一、Debezium二、依赖三、使用Debezium Format四、可用元数据五、Format参数六、重复的变更事件七、消费 Debezium Postgres Connector 产生的数据八、数据类型映射一、Debezium Debezium 是一个 CDC(…

rtsp视频在使用unity三维融合播放后的修正

1 rtsp 接入 我们使用unity UE 等三维渲染引擎中使用c编写插件来接入rtsp 视频。同时做融合的时候&#xff0c;和背景的三维颜色要一致&#xff0c;这就要使用视频融合修正技术。包括亮度&#xff0c;对比度&#xff0c;饱和度的修正。在单纯颜色上的修正可以简单使用rgb->…

Vue 复制、下载 图片和文字

目录 功能描述 图片的格式 复制图片和文字 第一种&#xff0c;直接复制&#xff08;不推荐&#xff09; 第二种&#xff0c;画图后复制 下载图片和文字 总结 功能描述 可以简单描述成复制图片和文字&#xff0c;下载图片和文字 前者还好说&#xff0c;比如在图片右键点…

文心一言 VS 讯飞星火 VS chatgpt (162)-- 算法导论13.1 2题

二、用go语言&#xff0c;对图13-1中的红黑树&#xff0c;画出对其调用TREE-INSERT 操作插人关键字 36 后的结果。如果插人的结点被标为红色&#xff0c;所得的树是否还是一棵红黑树?如果该结点被标为黑色呢? 文心一言&#xff0c;代码不能运行&#xff1a; 在Go语言中&…

Java中的基本数据类型

在Java中&#xff0c;基本数据类型有以下几种&#xff1a; 整数类型&#xff1a; byte&#xff1a; 1字节&#xff0c;在内存中范围为-128到127short&#xff1a; 2字节&#xff0c;在内存中范围为-32768到32767int&#xff1a; 4字节&#xff0c;在内存中范…

通过生成表征的自条件图像生成

文章目录 摘要1、简介2、相关工作3、方法4、结果4.1、设置4.2、无条件类别的生成4.3、无分类器指导4.4、消融实验4.5、计算成本4.6、定性结果 5、讨论 摘要 https://arxiv.org/pdf/2312.03701.pdf 本文提出了表示条件图像生成&#xff08;Representation-Conditioned Image Ge…

【Unity的实现好用的曲面UI_切角曲面边框流光效果_案例分享(内附源码)】

曲面UI_切角边框流光效果_案例分享 相关续篇效果图如下:Canvas 节点上的CurvedUISettings组件:CurvedUISettings组件CurvedUISettings代码如下:CurvedUIVertexEffectCurvedUIVertexEffect组件CurvedUIVertexEffect代码如下:最终效果:切角边框流光效果Shader代码解析实现思…

python绘制烟花特定爆炸效果,python画烟花绽放出字

大家好&#xff0c;小编来为大家解答以下问题&#xff0c;python绘制烟花特定爆炸效果&#xff0c;python画烟花绽放出字&#xff0c;今天让我们一起来看看吧&#xff01; 本文参考了https://blog.csdn.net/weixin_43943977/article/details/102691392博主的樱花以及https://bl…

Python办公自动化Day1

目录 文章声明⭐⭐⭐让我们开始今天的学习吧&#xff01;xlwt创建Excelxlrd读取Excelxlutils修改Excelxlwt设置样式常见的字体样式单元格宽高内容对齐方式设置单元格边框设置背景颜色样式整合起来的写法 文章声明⭐⭐⭐ 该文章为我&#xff08;有编程语言基础&#xff0c;非编…

Ubuntu及Docker 安装rabbitmq

安装ubuntu 前 先暴露端口&#xff1a; 5672 用于与mq服务器通信用 15672 管理界面使用的端口 docker命令&#xff1a;docker run -itd --name ubuntu -p 5672:5672 -p 15672:15672 ubuntu 进入docker : docker exec -it ubuntu /bin/bash 步骤&#xff1a; 1. 更新安装源…

otter-harbor同步

一. 部署及依赖 otter Github (一). 服务启动 1. mysql 5.6版本以上&#xff0c;作为 otter-manger 使用的数据库 # mysql docker run --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD123456 -d mysql:5.7 --character-set-serverutf8mb4 --collation-serverutf8mb4_un…

Alpha突触核蛋白神经退行性疾病

Alpha突触核蛋白科研背景 ● Alpha突触核蛋白约 15kDa, 140个氨基酸 ● StressMarq在E. coli中过表达人源基因然后将蛋白从细胞质基质中纯化出来 ● 未折叠的alpha突触核蛋白单体在12% SDS-PAGE上为~15 kDa的条带 StressMarq/欣博盛生物的Alpha突触核蛋白有以下两类&#xf…

电商物流信息化管理:淘宝商品快递费用的item_fee解决方案

随着电子商务的飞速发展&#xff0c;物流信息化管理在电商运营中扮演着越来越重要的角色。淘宝作为中国最大的电商平台之一&#xff0c;其商品快递费用是电商物流成本的关键部分。本文将探讨如何通过电商物流信息化管理和淘宝商品快递费用的item_fee解决方案&#xff0c;提高物…

Open3D 最小二乘拟合平面(直接求解法)

目录 一、算法原理二、代码实现三、结果展示本文由CSDN点云侠原创,原文链接。爬虫自重。 一、算法原理 平面方程的一般表达式为: A x + B y + C z