类和对象(下+)_const成员、初始化列表、友元、匿名对象

类和对象(下+)


文章目录

  • 类和对象(下+)
  • 前言
  • 一、const成员
  • 二、友元
    • 1.友元函数
    • 2.友元类
  • 三、初始化列表
  • 四、explicit关键字
  • 五、匿名对象
  • 总结


前言

static成员、内部类、const成员、初始化列表、友元、匿名对象


一、const成员

将const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。

其特性如下:

#define  _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}void Print()//Date* this{cout << _year << "年" << _month << "月" << _day << "日" << endl;}void Print()const //const Date* this{cout << _year << "年" << _month << "月" << _day << "日" << endl;}
private:int _year; // 年int _month; // 月int _day; // 日
};
int main()
{Date d1(2028, 1, 11);d1.Print();const Date d2(2028, 8, 18);d2.Print();return 0;
}

匹配原则(找最合适的)【权限不能放大】:
d1调用第一个Print(带const修饰)
d2调用第二个Print(不带const修饰
在这里插入图片描述
认为是2钟不同的类型,构成函数重载
但是在这里这样使用const是没有意义的。


写一个简单的顺序表

#define  _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <cassert>
using namespace std;class SeqList
{
public:void PushBack(int x){_a[_size++] = x;}size_t size()const{return _size;}int operator[](size_t i){assert(i < _size);return _a[i];}
private:int* _a = (int*)malloc(sizeof(int) * 10);size_t _size = 0;size_t _capacity = 0;
};int main()
{SeqList sl;sl.PushBack(1);sl.PushBack(2);sl.PushBack(3);for (size_t i = 0; i < sl.size(); i++){cout << sl[i]<<" ";//cout << sl.operator[](i) << " ";//与上式等价}return 0;
}

在这里插入图片描述
在这里插入图片描述
但是当我们需要修改sl里的内容时是不可以的,原因是重载operator[ ]返回的是int类型的_a[i]的拷贝,具有常性。
这里需要提一下:在这里插入图片描述
在这里插入图片描述
此时就可以进行修改了。


此时如果又需要一个专门用于打印的函数Print,并且在传参时防止sl对象被修改因此加以const修饰,但是此时又会出现新的报错,如下

#define  _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <cassert>
using namespace std;class SeqList
{
public:void PushBack(int x){_a[_size++] = x;}size_t size(){return _size;}int& operator[](size_t i){assert(i < _size);return _a[i];}
private:int* _a = (int*)malloc(sizeof(int) * 10);size_t _size = 0;size_t _capacity = 0;
};void Print(const SeqList& sl)
{for (size_t i = 0; i < s.size(); i++){cout << sl[i] << " ";}
}int main()
{SeqList sl;sl.PushBack(1);sl.PushBack(2);sl.PushBack(3);Print(sl);return 0;
}

在这里插入图片描述

报错原因出在:const对象调用非const对象,存在权限放大的问题
解决方法:

	size_t size() const{return _size;}//只读int& operator[](size_t i) const{assert(i < _size);return _a[i];}//读 or 写都可以  		//与上一个代码块构成函数重载int& operator[](size_t i){assert(i < _size);return _a[i];}

只需要在size()和[ ]重载函数中加以const修饰this指针,即可。

同样看下面这段日期类

class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}void Print()const{cout << _year << "年" << _month << "月" << _day << "日" << endl;}bool operator<(const Date& d)const{//this->Print();//this 是非const 的,可以调用const 的Printif (_year < d._year){return true;}else if (_year == d._year && _month < d._month){return true;}else if (_year == d._year && _month == d._month && _day < d._day){return true;}return false;}
private:int _year; // 年int _month; // 月int _day; // 日
};int main()
{const Date d1(2028, 1, 11);Date d2(2028, 8, 18);cout << (d1 < d2) << endl;cout << (d1.operator<(d2)) << endl;//与上行代码等价return 0;
}

const Date d1(2028, 1, 11);当d1加上const时,如果operator<不加const的话,会出现错误,原因依旧是d1的类型是const Date*类型,(属于权限放大)


二、友元

1.友元函数

友元函数的几点特性:

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

还是之前说的日期类,此时我们想要重载一下"<<"如下

	void operator<<(ostream& out){out << _year << "年" << _month << "月" << _day << "日" << endl;}

但是当我们想要使用重载的"<<“时,会出现以下情况:
在这里插入图片描述
在这里插入图片描述
因此为了能够正常使用,我们在全局定义关于”<<"的重载函数,但在全局定义又会出现无法访问成员变量的问题,因此此时就需要在Date中进行友元声明,这样在全局定义的函数就可以访问类成员变量了

friend void operator<<(ostream& out, const Date& d);

接下来为了满足cout<<d1<<d2;需要以上返回out,即:

	ostream& operator<<(ostream& out, const Date& d){out << d._year << "年" << d._month << "月" << d._day << "日" << endl;return out;}

在这里插入图片描述

2.友元类

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

  1. 友元关系是单向的,不具有交换性
  2. 友元关系不能传递
  3. 友元关系不能继承
class Time
{friend class Date1;
public:friend class Date;	// 声明日期类为时间类的友元类,//则在日期类中就直接访问Time类中的私有成员变量Time(int hour = 0, int minute = 0, int second = 0): _hour(hour), _minute(minute), _second(second){}
private:int _hour; int _minute; int _second;
};class Date1
{
public:Date1(int year, int month, int day){_year = year;_month = month;_day = day;}void SetTimeOfDate(int hour, int minute, int second){// 直接访问时间类私有的成员变量_t._hour = hour;_t._minute = minute;_t._second = second;}void Print()const{cout << _year << "年" << _month << "月" << _day << "日" << endl;}
private:int _year; // 年int _month; // 月int _day; // 日Time _t;
};

三、初始化列表

就像这样

public://初始化列表,是每个成员定义的地方Date(int year = 1900, int month = 1, int day = 1): _year(year), _month(month), _day(day){}
private://每个成员的声明int _year;int _month;int _day;

需要注意的几点是:

  1. 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
  2. 类中包含以下成员,必须放在初始化列表位置进行初始化:
    • 引用成员变量
    • const成员变量
    • 自定义类型成员(且该类没有默认构造函数时)
  3. 尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化。
  4. 成员变量在类中声明次序就是其在初始化列表中的初始化顺序与其在初始化列表中的先后次序无关
    在这里插入图片描述
    在构造函数的初始化列表阶段,对内置类型用随机值进行初始化,对自定义类型会调用它的默认构造
    在这里插入图片描述

当_day给了缺省值 int _day=1;
在这里插入图片描述
_day给了缺省值,把它初始化成1 了;接下来还要走函数体变为18,由此可知()缺省值就是给初始化列表用的!!

但是初始化列表也有不能解决初始化问题
(比如要求数组_array初始化一下(在函数体中中memset初始化))

四、explicit关键字

class A
{
public:A(int i):_a(i){cout << "A" << endl;}
private:int _a;
};int main()
{A aa1(1);A aa2 = 2;
}

在这里插入图片描述
因为

  • 单参数构造函数的隐式类型转换
  • 用2调用A构造函数生成一个临时对象,再用这个对象去拷贝构造aa2
  • 编译器会再优化,优化用2直接构造
    在这里插入图片描述
    >A& ref = 2;
    存在类型转换,会生成临时对象,2具有常量性,ref不能引用临时对象,也就是存在权限放大问题,加上const就可以了即:

const A& ref = 3;

紧接着还有一个问题,那么为什么这里ret可以引用2,因为因为这个单参数的函数支持隐式类型转换,单参数函数这个参数(2)的的整型值能转换成一个A的对象,就可以引用了。

如果说bu想让隐式类型转换发生,可以加关键字explicit,加在构造函数的位置

	explicit  A(int i):_a(i){cout << "A" << endl;}

此时在这里插入图片描述
在这里插入图片描述

以上是讨论的单参数的,那么如果是多参数的呢?

c++11支持多参数的转换

class B
{
public:B(int b1,int b2):_b1(b1),_b2(b2){cout << "B" << endl;}private:int _b1;int _b2;
};
int main()
{B bb1(1, 1);B bb2 = {2,2};const B& ref = { 3,3 };//同样的,不加const就不支持引用return 0;
}

道理同上,如果不想让隐式类型转换发生,使用关键字explicit

五、匿名对象

首先来比较一下有名对象和匿名对象:

  • 有名对象 特点:生命周期在当前局部域

A aa4(4);

  • 匿名对象 特点:生命周期只在这一行

A (5);

匿名对象可以用来传参,不用先创建变量,再传参

class A
{
public:explicit  A(int i):_a(i){cout << "A" << endl;}
private:int _a;
};class SeqList
{
public:void PushBack(A x){_a[_size++] = x;}size_t size() const{return _size;}A& operator[](size_t i) const{assert(i < _size);return _a[i];}private:A* _a = (A*)malloc(sizeof(A) * 10);size_t _size = 0;size_t _capacity = 0;
};int main()
{SeqList s;A aa3(3);s.PushBack(aa3);s.PushBack(A(4));//利用匿名对象,直接传参return 0;
}

当在一些特定场景下,适当使用匿名对象可以起到简化代码的作用。

总结

😁 ;

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

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

相关文章

如何使用共享GPU平台搭建LLAMA3环境(LLaMA-Factory)

0. 简介 最近受到优刻得的使用邀请&#xff0c;正好解决了我在大模型和自动驾驶行业对GPU的使用需求。UCloud云计算旗下的[Compshare](https://www.compshare.cn/? ytagGPU_lovelyyoshino_Lcsdn_csdn_display)的GPU算力云平台。他们提供高性价比的4090 GPU&#xff0c;按时收…

达梦数据库(DMDB)基本使用

达梦数据库&#xff08;DMDB&#xff09;作为一款高性能的国产关系型数据库管理系统&#xff0c;其丰富的功能和灵活的操作为用户提供了强大的数据管理支持。以下是对达梦数据库基础操作的详细介绍&#xff1a; 1. 模式&#xff08;Schema&#xff09;管理 在达梦数据库中&am…

Docker:镜像命令和容器命令

文章目录 镜像命令docker imagesdocker image inspeactdocker tag 容器命令docker rundocker ps 下面进入到关于镜像命令的学习中 镜像命令 docker images 这个命令的功能是列出本地的镜像 语法&#xff1a; docker images [options] [repository[:tag]]别名&#xff1a; …

计算机视觉中的low-level与 high-level任务

文章目录 low-level任务high-level任务区别联系others参考在计算机视觉领域中,low-level任务和high-level任务是两个重要的概念,他们分别涉及图像处理和分析的不同的层次。 low-level任务 low-level任务主要关注的是图像的底层特征,如颜色、纹理、边缘、形状等。通常涉及对…

js中-null不是基本类型

在JavaScript中&#xff0c;null 也不是基本类型&#xff08;也称作原始类型或简单数据类型&#xff09;&#xff0c;但它被归类为一种特殊的对象类型。这是一个历史遗留问题&#xff0c;也是JavaScript语言设计上的一些独特之处。在早期版本的ECMAScript规范中&#xff0c;typ…

【安装笔记-20240608-Linux-动态域名更新服务之YDNS】

安装笔记-系列文章目录 安装笔记-20240608-Linux-动态域名更新服务之YDNS 文章目录 安装笔记-系列文章目录安装笔记-20240608-Linux-动态域名更新服务之YDNS 前言一、软件介绍名称&#xff1a;YDNS主页官方介绍 二、安装步骤测试版本&#xff1a;openwrt-23.05.3-x86-64注册填…

Linux基础指令网络管理003

本章主要讲述如何进行网络诊断。 操作系统&#xff1a; CentOS Stream 9 操作步骤&#xff1a; 操作指令 ping&#xff1a; 测试网络连接的连通性和延迟。 [rootlocalhost ~]# ping 192.168.80.111 PING 192.168.80.111 (192.168.80.111) 56(84) 比特的数据。 64 比特&a…

从0~1开发财务软件

1.获取图形验证码接口 功能要求 1、随机生成6位字符 2、将字符生成base64位格式的图片&#xff0c;返回给前端 3、将生成的字符存储到redis中&#xff0c;用匿名身份id&#xff08;clientId&#xff09;作为key&#xff0c;验证码作为value。 clientId通过/login/getClien…

Python怎么调用JAR包:揭秘跨语言交互的奥秘

Python怎么调用JAR包&#xff1a;揭秘跨语言交互的奥秘 在编程领域&#xff0c;跨语言交互一直是一个热门话题。当Python需要调用Java编写的JAR包时&#xff0c;这种跨语言交互的需求就显得尤为突出。那么&#xff0c;Python究竟是如何调用JAR包的呢&#xff1f;本文将从四个方…

Angular知识概览

Angular 是一个由 Google 维护的开源前端框架&#xff0c;用于构建动态网页应用。以下是对 Angular 主要概念和特性的概览&#xff1a; 1. Angular 的核心概念 - 组件 (Component)&#xff1a;Angular 应用的基本构建块。每个组件包括一个 TypeScript 类&#xff0c;用于处理数…

1-5 C语言操作符

C语言提供了非常丰富的操作符&#xff0c;使得C语言使用起来非常的方便 算数操作符&#xff1a; 加 减 乘 除 取模 【 - * / %】 注&#xff1a;除号的两端都是整数的时候执行的是整数的除法&#xff0c;如果…

MATLAB基础应用精讲-【数模应用】主成分(pca)分析(附python代码实现)

目录 前言 知识储备 降维概述 算法原理 什么是PCA PCA降维过程 PCA算法数学步骤 选择主成分个数(即k的值) sklearn中参数的解释 数学模型 协方差 协方差矩阵 ​编辑 ​编辑 原理推导 ​编辑​编辑​编辑​编辑 实际操作 主成分分析的计算方法 方法1. 协方差+…

65. UE5 RPG 实现远程攻击

前面&#xff0c;我们实现了敌人的近战普通攻击&#xff0c;还兼容了对于没有武器的敌人的攻击。有近战就要有远程&#xff0c;这一篇&#xff0c;我们实现一下敌人的远程攻击。 首先&#xff0c;由于创建的资源增多&#xff0c;我们将GA和GE按敌人和英雄的类别分开&#xff0c…

MySQL—多表查询—联合查询

一、引言 之前学习了连接查询。现在学习联合查询。 union&#xff1a;联合、联盟 对于union查询&#xff0c;就是把多次查询的结果合并起来&#xff0c;形成一个新的查询结果集 涉及到两个关键字&#xff1a;union 和 union all 注意&#xff1a; union 会把上面两个SQL查询…

C++的STL 中 set.map multiset.multimap 学习使用详细讲解(含配套OJ题练习使用详细解答)

目录 一、set 1.set的介绍 2.set的使用 2.1 set的模板参数列表 2.2 set的构造 2.3 set的迭代器 2.4 set的容量 2.5 set的修改操作 2.6 set的使用举例 二、map 1.map的介绍 2.map的使用 2.1 map的模板参数说明 2.2 map的构造 2.3 map的迭代器 2.4 map的容量与元…

深圳中赢娱乐控股集团至江西省宜春市袁州区访问交流

2024年6月7日&#xff0c;深圳中赢娱乐控股集团受邀来到江西省宜春市袁州区就“短剧文旅”项目展开深度座谈&#xff0c;并与飞剑潭乡达成合作意向。 下午2:30&#xff0c;深圳中赢控股集团董事李平进带团队一行12人&#xff0c;访问宜春市袁州区&#xff0c;宜春市副市长谢萍、…

嵌入式学习——4——C++中的动态内存分配和回收(堆区)

1、内存的分配与回收 C语言中使用的是malloc和free函数进行动态内存分配和回收的。 C中依然可以使用上述的两个函数来完成动态内存分配和回收的。 C也给用户提供了两个关键字new、delete来完成动态内存分配和回收的 单个分配、回收 //在堆区申请了int类型的大小空间&#xff0c…

html--圣诞树

将以下代码保存到txt文件中&#xff0c;并改名为xx.html <html> <head> <title>圣诞树</title> <meta charset"utf-8" > <style> html, body { width: 100%; height: 100%; margin: 0; padding: 0; border: 0; } div { margin: …

【嵌入式DIY实例】-OLED显示LM35传感器数据

OLED显示LM35传感器数据 文章目录 OLED显示LM35传感器数据1、LM35传感器介绍2、硬件准备与接线2、代码实现本文将介绍如何将 ESP8266 NodeMCU 开发板(ESP12-E 模块)与 LM35 模拟温度传感器和 SSD1306 OLED 显示屏连接。 在本次实例中,SSD1306 OLED 显示屏(12864 像素)用于…

Java--命令行传参

1.有时你希望运行一个程序时再传递给它消息&#xff0c;这要靠传递命令行参数给main&#xff08;&#xff09;函数实现 2.选中文件右键找到如图选项并打开 3.在文件地址下输入cmd空格符号&#xff0c;再按回车调出命令窗口 4.如图一步步进行编译&#xff0c;在向其传入参数&…