当前位置: 首页 > news >正文

[C++基础]-类和对象(下)

前言

作者小蜗牛向前冲

名言我可以接受失败,但我不能接受放弃

  如果觉的博主的文章还不错的话,还请点赞,收藏,关注👀支持博主。如果发现有问题的地方欢迎❀大家在评论区指正。

目录

一、深入学习构造函数

1 构造函数赋值体

2 初始化列表 

3 使用初始化列表的三种情形

4 explicit关键字 

二、static成员

1 定义

2 特性

三.、友元 

1、友元函数

 2、友元类

四、内部类 

 五、匿名对象

六、拷贝对象时的一些编译器优化 


 

本期学习内容:深入学习构造函数,重点学习Static成员,了解什么是友元,内部类,匿名对象;清楚拷贝对象时编译器会做那些优化

一、深入学习构造函数

1 构造函数赋值体

我们对一个类创建对象的时候,编译器会通过调用构造函数,给对象中每个成员变量一个合适的初始值,虽然在调用构造函数后,对象中已经有了一个初始值,但是我们不能将这称之为对对象成员的初始化,只能将其称之为赋初始值,这是因为初始化只能初始化一次,而构造函数体内可以多次赋值。

那么在类中到底怎么进行初始化呢?这就不得不提我们的初始化列表。

2 初始化列表 

初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟 一个放在括号中的初始值或表达式。

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

这里我们知道什么是初始化列表,但是在到底有什么用了,下面我们将细节讨论一下,要用初始化列表的3种情况。

3 使用初始化列表的三种情形

 cosnt修饰的成员变量

对于cosnt修饰的变量只能初始化一次,而且在定义的时候必须初始化

 这里我在类A中写了构造函数,但是没有写初始化列表,编译器就报错了说我们没有对_a进行初始化,这也就说明构造函数(没有初始化列表)只能是是赋值能不能说是初始化。

引用成员变量

对于引用的成员变量,也在定义的时候要进行初始化,而仅仅简单的默认构造不能满足这个要求,所以就必须使用初始化列表来进行初始化。

自定义类型成员(且该类没有默认构造函数时)

在思考下面代码前,我们来理解一下什么是默认构造:

1 全缺省的构造函数

2 无参数的默认构造

3 我们没写编译器生成的

 这里我们用下面代码来说明:

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 
};

在类B中我定义了自定义类型A,那要对自定义类型A初始化,就要调用A的默认构造,但是我们发现A类中并没有默认构造(析构函数是半缺省的析构函数),所以我们必须要用初始化列表对A进行初始化,否则会报错

在知道上面这种情况必须使用初始化列表,下面我们在来看一段代码:

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();
}
A. 输出1  1
B.程序崩溃
C.编译不通过
D.输出1  随机值

这里会出现上面结果呢?

 这里我们发现_a被初始化为了1,但是_b确是随机值,这是为什么呢?

首先我们要明白,成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后 次序无关。

其次,我们上面在声明的时候是先声明_a2在声明_a1,而在初始化也要先初始化_a2,这里我们是用_a1初始化_a2的,但此时_a1是没有进行初始化自然是随机值,所以导致初始化后的_a2也是随机值,在初始化_a1的时候我们是传了一个1过去,所以_a1被初始化为1

总结

  1. 三种情况必须用初始化列表,所以我们在定义的类的时候最好写是初始化列表,一个类最好提供默认构造最好是全缺省。
  2. 如果在声明的时候写了缺省值和初始化列表,初始列表优先起作用。
  3. 当没有初始化列表的时候,对于内置类型有缺省值用缺省值,没有就用随机值初始化,而自定义类型就要调用默认构造函数,没有就会报错。    

4 explicit关键字 

构造函数不仅可以构造与初始化对象,对于单个参数或者除第一个参数无默认值其余均有默认值 的构造函数,还具有类型转换的作用。而关键字explicit具有禁止类型转换的作用

 单参数的构造

为了更好的理解,下面我们继续来看一下代码:

   Date d1(2023);//隐式类型的转换Date d2 = 2023;const Date& d3 = 2023;

 其中我们知道d1会调用他的构造函数进行初始化,但是d2这是怎么回事呢?

还能这样的吗?

看到这里我们很容易联想到拷贝构造的二种形式:

    //这里是拷贝构造的二种形势Date d4(d1);Date d5 = d1;

但是这里我们要区分好,其实d2就是一个直接构造得来的,这里发生隐式类型的转换(先将2023转换为日期类,然后在进行拷贝构造),但这里编译器会进行优化为直接构造。而d3也是类似形成的。

如果我们不想发生隐式类型的转换就可以在构造函数的前面加上关键字explicit

多参数的构造(c++11支持)

int main()
{Date d1 = { 2022, 10, 12 };// 等价于Date d2(2022, 10, 12 );const Date& d3 = { 2022, 10, 12 };return 0;
}

但我们的构造函数有多个参数的时候,也可以写成d1上面的形式,发生隐式类型的转换。

如果我们不想发生隐私类型的转换同样可以加上explicit。

explicit Date(int year, int month, int day)//不发生隐式类型的转换
{}

二、static成员

1 定义

声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用 static修饰的成员函数,称之为静态成员函数静态成员变量一定要在类外进行初始化

class A
{//静态成员函数static int Geta(){}
private://静态成员变量static int _a;
};
//静态成员变量的初始化
int A:: _a= 1;

2 特性

那我们为什么要定义静态的成员变量和成员函数呢?

其实就是因为静态的成员变量和成员函数会有他自己的特点:

1. 静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区。

       这也就说明他不需要多次的创建,减少了不必要的消耗。

2. 静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明。

3. 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问

4. 静态成员函数没有隐藏的this指针,不能访问任何非静态成员

5. 静态成员也是类的成员,受public、protected、private 访问限定符的限制

 应用场景:要求类对象只能在栈上面

// 要求类对象只能在栈上面
class A
{
public:static A GetObj(int a = 0){A aa(a);return aa;}private:A(int a = 0):_a(a){}private:int _a;
};int main()
{A aa3 = A::GetObj(10);return 0;
}

三.、友元 

友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以 友元不宜多用。 友元分为:友元函数和友元类。

简单的说友元是为了解决,类的私有变量在类外不能访问的问题。

1、友元函数

 这里我们可以想到我们在实现日期类对于<<和>>的重载的时候就用到了友元,当时尝试去重载operator<<,然后发现没办法将operator<<重载成成员函数。因为cout的 输出流对象和隐含的this指针在抢占第一个参数的位置。this指针默认是第一个参数也就是左操作 数了。但是实际使用中cout需要是第一个形参对象,才能正常使用。所以要将operator<<重载成 全局函数。但又会导致类外没办法访问成员,此时就需要友元来解决。operator>>同理。

	// 友元声明(类的任意位置)friend ostream& operator<<(ostream& out, const Date& d);friend istream& operator>>(istream& in, Date& d);
  • 友元函数可访问类的私有和保护成员,但不是类的成员函数 。
  • 友元函数不能用const修饰
  • 友元函数可以在类定义的任何地方声明,不受类访问限定符限制 。
  • 一个函数可以是多个类的友元函数。
  • 友元函数的调用与普通函数的调用原理相同。

 2、友元类

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;
};

友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。 友元关系是单向的,不具有交换性。 比如Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接 访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。 友元关系不能传递 如果C是B的友元, B是A的友元,则不能说明C时A的友元。 友元关系不能继承。

四、内部类 

概念

如果一个类定义在另一个类的内部,这个内部类就叫做内部类。内部类是一个独立的类, 它不属于外部类,更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越 的访问权限。

注意

内部类就是外部类的友元类,参见友元类的定义,内部类可以通过外部类的对象参数来访 问外部类中的所有成员。但是外部类不是内部类的友元 ,也就是当B是A的内部类,B可以通过A的对象访问A的成员,而反过来就不可以。

特性:

  1.   内部类可以定义在外部类的public、protected、private都是可以的。
  2.   注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名。
  3.   sizeof(外部类)=外部类,和内部类没有任何关系。(这里也就是说求外部类的大小时候,不包含内部类的大小
class MyA
{
private:static int k;int h;
public:class MyB // B天生就是A的友元{public:void foo(const MyA& a){cout << k << endl;//OKcout << a.h << endl;//OK}};
};
int MyA::k = 1;
int main()
{MyA::MyB b;b.foo(MyA());return 0;
}

 五、匿名对象

 C++中的匿名对象是指在创建对象时,没有将其赋值给任何变量,也没有使用任何名称引用它的对象。这种对象通常只在单个语句中使用,并且在该语句执行完成后被销毁。因为它们没有名称,所以它们只能通过创建它们的表达式来访问。例如,在以下代码中:

class A
{
public:A(int a = 0):_a(a){cout << "A(int a)" << endl;}~A(){cout << "~A()" << endl;}
private:int _a;
};int main()
{// 有名对象A aa0;A aa1(1);A aa2 = 2;// 匿名对象 --声明周期当前这一行A();A(3);
}

这里我们要注意,匿名对象的声明周期就在当前行,如果出来当前行就要被销毁。 

那匿名对象有什么用吗?

总的来是就二点:

1 可以触发编译器的优化(下面会分析)

2 让代码看起来根据合理

	A f2(){//A aa(11);//return aa(11);//用匿名对象触发优化return A(11);}
class Solution {
public:int Sum_Solution(int n) {//...return n;}
};int main()
{//Solution so;//so.Sum_Solution(10);//用匿名对象写起来更加舒服Solution().Sum_Solution(10);return 0;
}

六、拷贝对象时的一些编译器优化 

在传参和传返回值的过程中,一般编译器会做一些优化,减少对象的拷贝,这个在一些场景下还 是非常有用的。

下面代码的类A假设已经定义好了

场景1

int main()
{// 优化场景1A aa1 = 1;  // A tmp(1) + A aa1(tmp) -> 优化 A aa1(1)return 0;
}

 场景1本应该先发生对1进行隐式类型转换,在进行拷贝构造,但是编译器直接优化为拷贝构造,因为隐式类型的提升也是要消耗资料的。

但是我们要注意:下面怎么写就不能进行优化了。

int main()
{// 优化场景1A aa1;aa1 = 10;return 0;
}

 场景2

void f1(A aa)
{}A f2()
{A aa;return aa;
}A f3()
{/*A aa(10);return aa;*/return A(10);
}int main()
{f1(A(1));  // 构造 + 拷贝构造  -> 优化 构造f1(1);  // 构造 + 拷贝构造  -> 优化 构造f2();	  // 构造+拷贝构造A ret = f2(); // 构造+拷贝构造+拷贝构造 ->优化 构造+拷贝构造A ret = f3();  //  构造+拷贝构造+拷贝构造 -> 优化 -> 构造return 0;
}

 

相关文章:

[C++基础]-类和对象(下)

前言 作者&#xff1a;小蜗牛向前冲 名言&#xff1a;我可以接受失败&#xff0c;但我不能接受放弃 如果觉的博主的文章还不错的话&#xff0c;还请点赞&#xff0c;收藏&#xff0c;关注&#x1f440;支持博主。如果发现有问题的地方欢迎❀大家在评论区指正。 目录 一、深入学…...

NP完全性PART1:多项式时间与形式化语言体系

算法导论第三版&#xff0c;CH34笔记 NP完全性 Chapter Introduction 一般来说&#xff0c;我们认为可以在多项式时间内求解的问题是易处理的问题&#xff0c;在超多项式时间内解决的问题是不易处理的问题。 下面列出的几对问题&#xff0c;前者可以用多项式时间算法求解&…...

685页40万字某省市场监管智慧应用一体化项目(word可编辑)

1.2.3.1 数字XX公共能力建设现状 1.2.3.1.1 数字XX通用基础应用平台现状 通用基础应用平台提供具有共性特征的跨部门、跨层级业务应用&#xff0c;与本项目有关的平台包括某省网上办事大厅、某省政务服务 APP 统一平台&#xff08;X政通 APP&#xff09;、某省公共信用信息平…...

【cutlass】cuTe 01 layout

简介 Layout将坐标映射到地址空间&#xff0c;其对阵列单元在内存中如何排布进行抽象&#xff0c;并提供了获取多维阵列的接口。用户可以以正常的方式&#xff08;不需要做复杂的地址计算&#xff09;写多维阵列存取代码&#xff0c;对于存取的不同方式可以改变Layout来实现&a…...

迁移学习

迁移学习 什么是迁移学习 迁移学习【斯坦福21秋季&#xff1a;实用机器学习中文版】 迁移学习&#xff08;Transfer Learning&#xff09;是一种机器学习方法&#xff0c;它通过将一个领域中的知识和经验迁移到另一个相关领域中&#xff0c;来加速和改进新领域的学习和解决问…...

基于session实现共享登录

基于session实现登录 1.发送短信验证码 Override public Result sendCode(String phone, HttpSession session) {//1.校验手机号是否合规if (RegexUtils.isPhoneInvalid(phone)) {//2.不合规直接返回 错误信息return Result.fail("手机号错误");}//3.如果合规生成验…...

Hudi学习笔记1

使用注意 从 0.10.0 版本开始&#xff0c;primaryKey 为必须的&#xff0c;不再支持没有主键的表。 primaryKey、primaryKey 和 type 均大小写敏感。 对于 MOR 类型的表&#xff0c;preCombineField 为必须的。 当设置 primaryKey、primaryKey 或 type 等 hudi 配置时&#…...

嚯——ChatGPT是很强,但也会胡说八道。。。

现在的ChatGPT确实强&#xff0c;但是也会一本正经的胡说八道&#xff0c;例如它回答“nineteen”中有12个字母、或是旗鱼是哺乳动物…… 尽管ChatGPT可以生成流畅甚至优雅的散文&#xff0c;轻松通过困扰了AI领域超过70年的图灵测试基准&#xff0c;但它也可能看起来非常愚蠢…...

Springboot常用注解总结

目录 一、什么是Spring Boot二、Spring常用注解三、Spring Boot常用注解1、SpringBootApplication2、ImportAutoConfiguration3、SpringBootConfiguration4、ImportResource5、PropertySource6、PropertySources7、Role8、Scope9、Lazy11、Profile12、DependsOn13、PostConstru…...

让chatGPT给我写一个CSS,我太蠢了

前言 CSS这东西&#xff0c;让AI写的确有点难度&#xff0c;毕竟它写出来的东西&#xff0c;没办法直接预览&#xff0c;这是其次。重要的是CSS这东西怎么描述&#xff0c;不好描述啊&#xff0c;比如我让他给我制作一个这样的效果出来&#xff0c;没办法描述&#xff0c;所以…...

华为OD题目:分奖金

分奖金 知识点栈时间限制: 1s 空间限制: 256MB 限定语言: 不限 题目描述: 公司老板做了一笔大生意&#xff0c;想要给每位员工分配一些奖金&#xff0c;想通过游戏的方式来决定每个人分多少钱。 按照员工的工号顺序&#xff0c;每个人随机抽取一个数字。按照工号的顺序往后排列…...

【算法题】2401. 最长优雅子数组

插&#xff1a; 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 坚持不懈&#xff0c;越努力越幸运&#xff0c;大家一起学习鸭~~~ 题目&#xff1a; 给你一个由 正 整数组成的数组 num…...

【Vue】Vue快速入门

Vue快速入门 Vue.js的引入 要先有一个vue.js文件&#xff0c;可以在vue官网下载&#xff0c;将其复制到项目中并在html页面中进行引入&#xff1a; 在head标签内引入&#xff0c;src内是vue的路径 <!-- 引入vue.js--><script language"JavaScript" s…...

二本菜鸡,颓废两年的自我救赎

大家好&#xff0c;我是帅地。 随着校招的结束&#xff0c;帅地的星球里也有不少小伙伴前来报喜&#xff0c;今天这篇&#xff0c;是星球一个颓废两年同学的自我救赎之路&#xff0c;我觉得他的经历和很多人一样&#xff0c;前两年可能就颓废了&#xff0c;后面才后知后觉&…...

Spring boot 常用注解

SpringBootApplication&#xff1a;用于启动Spring Boot应用程序的主类上&#xff0c;组合了Configuration、EnableAutoConfiguration和ComponentScan三个注解。 RestController &#xff1a;修饰类&#xff0c;使用RestController注解的Controller中的方法默认返回值都会以JS…...

mysql从零开始(05)----锁

全局锁 使用 # 启用全局锁 flush tables with read lock # 释放全局锁 unlock tables开启全局锁后&#xff0c;整个数据库就处于只读状态了&#xff0c;这种状态下&#xff0c;对数据的增删改操作、对表结构的更改操作都会被阻塞。 另外&#xff0c;当会话断开&#xff0c;全…...

《Linux 内核设计与实现》03. 进程管理

文章目录 进程描述符及任务结构分配进程描述符进程描述符的存放进程状态设置当前进程状态进程上下文进程家族树 进程创建线程在 Linux 中的实现创建线程内核线程 进程终结删除进程描述符孤儿进程 进程描述符及任务结构 内核把进程存放在任务队列&#xff08;task list&#xf…...

深入探究HDFS:高可靠、高可扩展、高吞吐量的分布式文件系统【上进小菜猪大数据系列】

上进小菜猪&#xff0c;沈工大软件工程专业&#xff0c;爱好敲代码&#xff0c;持续输出干货。 引言 在当今数据时代&#xff0c;数据的存储和处理已经成为了各行各业的一个关键问题。尤其是在大数据领域&#xff0c;海量数据的存储和处理已经成为了一个不可避免的问题。为了应…...

GIMP制作艺术字技巧

GIMP下载官网 https://www.gimp.org/downloads/ 我使用的版本 2.10.32 字体下载 https://ziyouziti.com/index-index-all.html 下载解压之后会有otf、ttf等字体文件&#xff0c;需要拷贝到gimp当前用户目录 C:\Users\用户名\AppData\Roaming\GIMP\2.10\fonts GIMP绘制字…...

Redis 布隆过滤器总结

Redis 布隆过滤器总结 适用场景 大数据判断是否存在来实现去重&#xff1a;这就可以实现出上述的去重功能&#xff0c;如果你的服务器内存足够大的话&#xff0c;那么使用 HashMap 可能是一个不错的解决方案&#xff0c;理论上时间复杂度可以达到 O(1) 的级别&#xff0c;但是…...

挑战杯推荐项目

“人工智能”创意赛 - 智能艺术创作助手&#xff1a;借助大模型技术&#xff0c;开发能根据用户输入的主题、风格等要求&#xff0c;生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用&#xff0c;帮助艺术家和创意爱好者激发创意、提高创作效率。 ​ - 个性化梦境…...

UDP(Echoserver)

网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法&#xff1a;netstat [选项] 功能&#xff1a;查看网络状态 常用选项&#xff1a; n 拒绝显示别名&#…...

django filter 统计数量 按属性去重

在Django中&#xff0c;如果你想要根据某个属性对查询集进行去重并统计数量&#xff0c;你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求&#xff1a; 方法1&#xff1a;使用annotate()和Count 假设你有一个模型Item&#xff0c;并且你想…...

TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案

一、TRS收益互换的本质与业务逻辑 &#xff08;一&#xff09;概念解析 TRS&#xff08;Total Return Swap&#xff09;收益互换是一种金融衍生工具&#xff0c;指交易双方约定在未来一定期限内&#xff0c;基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...

自然语言处理——Transformer

自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效&#xff0c;它能挖掘数据中的时序信息以及语义信息&#xff0c;但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN&#xff0c;但是…...

华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建

华为云FlexusDeepSeek征文&#xff5c;DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色&#xff0c;华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型&#xff0c;能助力我们轻松驾驭 DeepSeek-V3/R1&#xff0c;本文中将分享如何…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

NPOI Excel用OLE对象的形式插入文件附件以及插入图片

static void Main(string[] args) {XlsWithObjData();Console.WriteLine("输出完成"); }static void XlsWithObjData() {// 创建工作簿和单元格,只有HSSFWorkbook,XSSFWorkbook不可以HSSFWorkbook workbook new HSSFWorkbook();HSSFSheet sheet (HSSFSheet)workboo…...

Caliper 配置文件解析:fisco-bcos.json

config.yaml 文件 config.yaml 是 Caliper 的主配置文件,通常包含以下内容: test:name: fisco-bcos-test # 测试名称description: Performance test of FISCO-BCOS # 测试描述workers:type: local # 工作进程类型number: 5 # 工作进程数量monitor:type: - docker- pro…...

c++第七天 继承与派生2

这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分&#xff1a;派生类构造函数与析构函数 当创建一个派生类对象时&#xff0c;基类成员是如何初始化的&#xff1f; 1.当派生类对象创建的时候&#xff0c;基类成员的初始化顺序 …...