C++:类和对象(下)

目录

1. 再谈构造函数

1.1构造函数题赋值

1.2初始化列表

初始化列表有什么用呢?

1.3 explicit关键字

2. Static成员

2.1概念

2.2特性

3. 友元

3.1友元函数

3.2友元类

4. 内部类(了解)

5.匿名对象

6.拷贝对象时的一些编译器优化


学习目标

  • 1. 再谈构造函数
  • 2. Static成员
  • 3. 友元
  • 4. 内部类
  • 5.匿名对象
  • 6.拷贝对象时的一些编译器优化

1. 再谈构造函数

1.1构造函数题赋值

构造函是编译器调用其时,给对象中各成员变量一个合适的初始值

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初始化列表

语法:以一个冒号(:)开始,用逗号(‘,’)分割成员列表,成员变量的后面跟一个放在括号中的初始值或表达式

示例:

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成员变量
  • 自定义类型成员(且该类没有构造函数)


原因:

--引用的成员变量:引用必须在定义的时候初始化

--const成员变量:const必须在定义的时候初始化(只有一次机会)

--自定义类型成员(没有默认构造函数)必须初始化:和下面类似,若Date类里面有一个自定义类型的成员变量 Time _t ,若其没有适合的构造函数,会使得Date的实例化不了,所以其必须初始化

3. 尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化

4.成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关

示例:

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  随机值

答案是D,类里先声明了_a2,先初始化_a2,_a2是用_a1初始化(_a1此时是随机值),

                                           然后初始化_a1, _a1用1初始化

所以最后,_a1 = 1, _a2  = 随机值;

初始化列表有什么用呢?

原来的构造函数是函数体内初始化,总会面临一些不好处理的情况(如下),因此引入初始化列表解决问题

--可以用来处理默认构造函数不适配的情况,当一个类(Time)给了一个不适配的构造函数,我们可以用初始化列表处理这个类

问题引入:现给一个Time类,提供一个带参数的构造函数,Date类里面包含Time类,我们该如何初始化Time类?

1.正常情况:Time类没有默认构造函数,编译器要处理自定义类型,会生成一个默认构造函数,但编译器又不会处理内置类型

示例:

class Time 
{
public://不给构造函数
private:int _hour;
};class Date
{
public:private:int _year;Time _t;
};int main() 
{Date d1;//Date和Time类都不给默认构造函数return 0;
}

这里看起来编译器什么都没做:

实际上--用Date去实例化d1,它的成员变量_year(不处理),

_t(自定义类型Time)--去调用它的默认构造函数,但Time没给默认构造函数,所以编译器自动生成一个默认构造函数,但其不会处理内置类型,Time的成员变量又是内置类型

所以看起来编译器什么都没做

2.Time类给一个不适配的构造函数:

class Time 
{
public:Time(int hour) //给个不适配的构造函数{_hour = hour;}
private:int _hour;
};class Date
{
public:private:int _year;//Time _t;
};int main() 
{Date d1;return 0;
}

这里就会发现,我们处理不了Time类,并且实例化不了Date类

然后我们使用初始化列表解决:

class Time 
{
public:Time(int hour) //不适配的构造函数{_hour = hour;}
private:int _hour;
};class Date
{
public://要初始化_t只能通过初始化列表Date(int year, int hour):_t(hour)//初始化列表解决自定义类型成员变量没有构造函数的情况{_year = year;}private:int _year;Time _t;
};int main() 
{Date d(2023,1);//给Date构造函数传值return 0;
}

这里就解决了,自定义类型的成员变量没有构造函数的情况

总结

1.自定义类型成员,推荐使用初始化列表初始化

2.初始化列表可以认为是成员变量定义的地方

1.3 explicit关键字

功能:explicit是阻止隐式类型转换的

隐式类型转换:

int i = 10;
double d = i;
//会产生临时变量tmp,把i转换为double类型后再拷贝给d

验证:

int i = 10;
double& d = i;
//这里引用是引用的临时变量,临时变量具有常性,不能引用

这里加个const也行,因为现在是权限的平移(const 修饰的变量不能修改,常量也不能修改,加上const后再引用也不能修改)

有什么用呢?示例:

现在加上const:

这里做到的优化:可以不用构造一个string对象就能传参

也说明了:传参尽量用引用,用引用尽量加上const

传引用相比于传值调用:不用创建临时变量去拷贝,会更快一些

接下来看下面这段代码:

class Date
{
public://构造函数Date(int year):_year(year){cout << "Date(int year)" << endl;}//拷贝构造Date(const Date& d) {cout << "Date(const Date& d)" << endl;}private:int _year;
};int main() 
{Date d1(2023);//直接调用构造Date d2 = 2023;//构造 + 拷贝构造 + 优化  ==》 直接调用构造(隐式类型转换)return 0;
}

   1-- Date d1(2023);//直接调用构造
   2--Date d2 = 2023;//构造 + 拷贝构造 + 优化  ==》 直接调用构造(隐式类型转换)

   2中会先创建一个Date类型的临时变量tmp,然后把2023转换为Date类型拷贝构造给d2

现在在构造函数上为其加上  explicit关键字,就能阻止隐式类型的转换

class Date
{
public://构造函数explicit Date(int year)//加上explicit:_year(year){cout << "Date(int year)" << endl;}//拷贝构造Date(const Date& d) {cout << "Date(const Date& d)" << endl;}private:int _year;
};int main() 
{Date d1(2023);//直接调用构造Date d2 = 2023;//构造 + 拷贝构造 + 优化  ==》 直接调用构造(隐式类型转换)return 0;
}

2. Static成员

2.1概念

声明为static的类成员称为类的静态成员

用static修饰的成员变量,称之为静态成员变量;

用static修饰的成员函数,称之为静态成员函数。

注:静态成员变量一定要在类外进行初始化

问题1:实现一个类,来计算程序创建了多少个类对象

class A 
{
public:A() { ++_count; }//构造函数A(const A& a) { ++_count; }//拷贝构造函数~A() { --_count; }//析构函数//private:static int _count;//声明int _a;
};//类外定义初始化
int A::_count = 0;

2.2特性

  • 1. 静态成员为所有类对象所共享,也属于类,不属于某个具体的对象,存放在静态区
  • 2. 静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明
  • 3. 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问
  • 4. 静态成员函数没有隐藏的this指针,不能访问任何非静态成员
  • 5. 静态成员也是类的成员,受public、protected、private 访问限定符的限制

静态成员为所有类对象所共享:

A a1;
A a2;
A a3;

这里使用A类实例化了3个对象,_count是静态成员变量,这3个类对象共享

                                                ,_a是每个对象独立有的

若static修饰的成员变量是私有的,我们该怎么访问呢?

--可以使用static修饰的成员函数来获取:

class A 
{
public:A() { ++_count; }//构造函数A(const A& a) { ++_count; }//拷贝构造函数~A() { --_count; }//析构函数//静态成员函数    ---   没有this指针static int GetCount() {return _count;}private://静态成员变量,属于整个类,在静态区static int _count;//声明int _a;
};//类外定义初始化
int A::_count = 0;int main() 
{A a1;cout << A::GetCount() << endl;return 0;
}

问题:
1. 静态成员函数可以调用非静态成员函数吗?

--不能,静态成员函数没有隐藏的this指针,不能访问任何非静态成员
2. 非静态成员函数可以调用类的静态成员函数吗?

--可以,静态成员函数为所有类对象所共享

2.设计一个只能在栈上定义的对象的类

示例

class StackOnly 
{
public:static StackOnly CreateObj() {StackOnly s;return s;}private:StackOnly()//构造函数:_x(0),_y(0){}
private:int _x;int _y;
};int main() 
{//显示定义的构造函数不加private,创建的对象可以在栈,静态区//StackOnly s1;		//栈上//static StackOnly s2;//静态区上//显示定义构造函数并加上private,让其只能通过调用成员函数来创建对象//要调用这个函数我们加上static即可StackOnly s = StackOnly::CreateObj();return 0;
}

3. 友元

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

        分类:友元函数和友元类

3.1友元函数

功能:友元函数可以直接访问类的私有成员

语法:它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字

示例:

class A 
{friend int sum(const A& a);
public:private:int _a;int _b;
};int sum(const A& a) 
{return a._a + a._b;
}

补充:

  • 友元函数可访问类的私有和保护成员,但不是类的成员函数
  • 友元函数不能用const修饰
  • 友元函数可以在类定义的任何地方声明,不受类访问限定符限制
  • 一个函数可以是多个类的友元函数
  • 友元函数的调用与普通函数的调用原理相同

3.2友元类

        友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。

  • 友元关系是单向的,不具有交换性。

如:Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接
访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。

  • 友元关系不能传递,如果C是B的友元, B是A的友元,则不能说明C时A的友元

4. 内部类(了解)

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


注意:内部类就是外部类的友元类,参见友元类的定义,内部类可以通过外部类的对象参数

来访问外部类中的所有成员。但是外部类不是内部类的友元。

特性:
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)
{
cout << k << endl;//OK
cout << a.h << endl;//OK
}
};
};
int A::k = 1;
int main()
{A::B b;b.foo(A());return 0;
}

5.匿名对象

语法:

Date(2023);//匿名对象

特点:生命周期只有这一行

作用:当我们只想调用类里面的函数的时候,就可以使用匿名对象,而不用实例化对象

//使用匿名对象调用类里的成员函数Date().PrintYear(2023);//实例化对象调用类里的成员函数Date d1(2023);d1.PrintYear(2023);

6.拷贝对象时的一些编译器优化

在传参和传返回值的过程中,一般编译器会做一些优化,减少对象的拷贝

优化:连续一个表达式步骤中,连续的构造一般都会优化:例如构造+拷贝构造 --> 构造

class A
{
public:A(int a = 0)//构造:_a(a){cout << "A(int a)" << endl;}A(const A& aa)//拷贝构造:_a(aa._a){cout << "A(const A& aa)" << endl;}A& operator=(const A& aa)//运算符重载{cout << "A& operator=(const A& aa)" << endl;if (this != &aa){_a = aa._a;}return *this;}~A()//析构{cout << "~A()" << endl;}
private:int _a;
};void f1(A aa)
{}A f2()
{A aa;return aa;
}
int main()
{// 传值传参A aa1;f1(aa1);cout << endl;// 传值返回f2();cout << endl;// 隐式类型,连续构造+拷贝构造->优化为直接构造f1(1);// 一个表达式中,连续构造+拷贝构造->优化为一个构造f1(A(2));cout << endl;// 一个表达式中,连续拷贝构造+拷贝构造->优化一个拷贝构造A aa2 = f2();cout << endl;// 一个表达式中,连续拷贝构造+赋值重载->无法优化aa1 = f2();cout << endl;return 0;
}

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

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

相关文章

在访问一个网页时弹出的浏览器窗口,如何用selenium 网页自动化解决?

相信大家在使用selenium做网页自动化时&#xff0c;会遇到如下这样的一个场景&#xff1a; 在你使用get访问某一个网址时&#xff0c;会在页面中弹出如上图所示的弹出框。 首先想到是利用Alert类来处理它。 然而&#xff0c;很不幸&#xff0c;Alert类处理的结果就是没有结果…

Hive-安装与配置(1)

&#x1f947;&#x1f947;【大数据学习记录篇】-持续更新中~&#x1f947;&#x1f947; 个人主页&#xff1a;beixi 本文章收录于专栏&#xff08;点击传送&#xff09;&#xff1a;【大数据学习】 &#x1f493;&#x1f493;持续更新中&#xff0c;感谢各位前辈朋友们支持…

OpenCV(十):图像缩放、翻转、拼接的介绍与使用

目录 &#xff08;1&#xff09;图像缩放&#xff1a;resize() &#xff08;2&#xff09;图像翻转&#xff1a; flip() &#xff08;3&#xff09;图像拼接&#xff1a;hconcat() 和vconcat() &#xff08;1&#xff09;图像缩放&#xff1a;resize() 使用 cv2.resize() 函…

leetcode 1022.从根到叶的二进制数之和

⭐️ 题目描述 &#x1f31f; leetcode链接&#xff1a;https://leetcode.cn/problems/sum-of-root-to-leaf-binary-numbers/description/ 代码&#xff1a; class Solution { public:int sum (TreeNode* root , int num 0) {if (root nullptr) {return 0;}int cur num r…

【数学建模竞赛】各类题型及解题方案

评价类赛题建模流程及总结 建模步骤 建立评价指标->评价体系->同向化处理&#xff08;都越多越好或越少越少&#xff09;->指标无量纲处理 ->权重-> 主客观->合成 主客观评价问题的区别 主客观概念主要是在指标定权时来划分的。主观评价与客观评价的区别…

QUdpSocket Class

继承自 QAbstractSocket 类 QUdpSocket类提供UDP套接字。 UDP(用户数据报协议)是一种轻量级、不可靠、面向数据报、无连接的协议。它可以在可靠性不重要的情况下使用。QUdpSocket是QAbstractSocket的一个子类&#xff0c;它允许您发送和接收UDP数据报。 使用这个类最常见的方法…

一文1800字从0到1使用Python Flask实战构建Web应用

Python Flask是一个轻量级的Web框架&#xff0c;它简单易用、灵活性高&#xff0c;适用于构建各种规模的Web应用。本文将介绍如何使用Python Flask框架来实战构建一个简单的Web应用&#xff0c;并展示其基本功能和特性。 第一部分&#xff1a;搭建开发环境 在开始之前我们需要…

docker部署nginx,部署springboot项目,并实现访问

一、先部署springboot项目 1、安装docker&#xff1a; yum install docker -y 2、启动docker&#xff1a; service docker start 重启&#xff1a; service docker restart 3、查看版本&#xff1a; docker -v 4、使设置docker.service生效&#xff08;路径&#xff1a;…

游戏思考30(补充版):关于逆水寒铁牢关副本、白石副本和技能的一些注释(2023/0902)

前期介绍 我是一名逆水寒的玩家&#xff0c;做一些游戏的笔记当作攻略记录下来&#xff0c;荣光不朽-帝霸来源视频连接 传送门 一、旧版铁牢关&#xff08;非逆水寒老兵服&#xff09; &#xff08;1&#xff09;老一&#xff1a;巨鹰 1&#xff09;机制一&#xff1a;三阵风…

Bert和LSTM:情绪分类中的表现

一、说明 这篇文章的目的是评估和比较 2 种深度学习算法&#xff08;BERT 和 LSTM&#xff09;在情感分析中进行二元分类的性能。评估将侧重于两个关键指标&#xff1a;准确性&#xff08;衡量整体分类性能&#xff09;和训练时间&#xff08;评估每种算法的效率&#xff09;。…

TDesign在按钮上加入图标组件

在实际开发中 我们经常会遇到例如 添加或者查询 我们需要在按钮上加入图标的操作 TDesign自然也有预备这样的操作 首先我们打开文档看到图标 例如 我们先用某些图标 就可以点开下面的代码 可以看到 我们的图标大部分都是直接用tdesign-icons-vue 导入他的组件就可以了 而我…

LabVIEW计算测量路径输出端随机变量的概率分布密度

LabVIEW计算测量路径输出端随机变量的概率分布密度 今天&#xff0c;开发算法和软件来解决计量综合的问题&#xff0c;即为特定问题寻找最佳测量算法。提出了算法支持&#xff0c;以便从计量上综合测量路径并确定所开发测量仪器的测量误差。测量路径由串联的几个块组成&#x…

Flutter启动页

效果图 import dart:async; import package:flutter/cupertino.dart; import package:flutter/material.dart; import jumpPage.dart;class TransitPage extends StatefulWidget {const TransitPage({super.key});overrideState<TransitPage> createState() > _Trans…

【项目 计网8】4.23 TCP状态转换 4.24半关闭、端口复用

文章目录 4.23 TCP状态转换关于三次握手四次挥手 4.24半关闭、端口复用端口复用 4.23 TCP状态转换 2MSL(Maximum Segment Lifetime) 主动断开连接的一方&#xff0c;最后进入一个TIME_WAIT状态&#xff0c;这个状态会持续&#xff1a;2msl msl&#xff1a;官方建议&#xff1a;…

Private market:借助ZK实现的任意计算的trustless交易

1. 引言 Private market&#xff0c;借助zk-SNARKs和以太坊来 隐私且trustlessly selling&#xff1a; 1&#xff09;以太坊地址的私钥&#xff08;ECDSA keypair&#xff09;2&#xff09;EdDSA签名3&#xff09;Groth16 proof&#xff1a;借助递归性来匿名交易Groth16 proo…

NCCoE发布“向后量子密码学迁移”项目进展情况说明书

近日&#xff0c;NIST下属的国家网络安全中心&#xff08;NCCoE&#xff09;发布了一份向后量子密码学迁移&#xff08;Migration to Post-Quantum Cryptography&#xff09;项目情况说明书。该文档简要概述了向后量子密码学迁移项目的背景、目标、挑战、好处和工作流程&#x…

flutter plugins插件【三】【Flutter Intl】

3、 Flutter Intl 多语言国际化 在Android Studio中菜单Tools找到flutter intl创建多语言配置。 创建后会在pubspec.yaml出现 flutter_intl:enabled: true 在工程的lib会生成l10n与generated文件夹 l10n包含 intl_en.arb intl_zn.arb 我们在intl_en.arb添加 { home: &quo…

说说HTTP 和 HTTPS 有什么区别?

分析&回答 http协议 超文本传输协议&#xff0c;是互联网上应用最多的协议&#xff0c;基于TCP/IP通讯协议来传递信息&#xff0c;用于从WWW服务器传输超文本到本地浏览器的传输协议。 https协议 我们可以将其看作是以安全为目标的http协议。在http协议的基础上增加了S…

stm32 iap sd卡升级

参考&#xff1a;STM32F4 IAP 跳转 APP问题_stm32程序跳转_古城码农的博客-CSDN博客 app程序改两个位置 1.程序首地址&#xff1a; 2.改中断向量表位移&#xff0c;偏移量和上面一样就可以 然后编译成bin文件就可以了

K-Means(K-均值)聚类算法理论和实战

目录 K-Means 算法 K-Means 术语 K 值如何确定 K-Means 场景 美国总统大选摇争取摆选民 电商平台用户分层 给亚洲球队做聚类 ​编辑 其他场景 K-Means 工作流程 K-Means 开发流程 K-Means的底层代码实现 K-Means 的评价标准 K-Means 算法 对于 n 个样本点来说&am…