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

news2024/11/26 13:51:28

类和对象(下+)


文章目录

  • 类和对象(下+)
  • 前言
  • 一、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 的Print
		if (_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.coloradmin.cn/o/1808826.html

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

相关文章

cleanmymac清理时要一直输入密码 CleanMyMac X一直提示输入密码的解决方案

CleanMyMac X是一款专业的Mac清理软件&#xff0c;可智能清理mac磁盘垃圾和多余语言安装包&#xff0c;快速释放电脑内存&#xff0c;轻松管理和升级Mac上的应用。同时CleanMyMac X可以强力卸载恶意软件&#xff0c;修复系统漏洞&#xff0c;一键扫描和优化Mac系统。 在使用Cle…

LeetCode | 2022.将一维数组转变为二维数组

这道题思路比较简单&#xff0c;比较容易想到的是先判断m和n构成的二维数组在形式上是否可以由原来的数组转变而成&#xff0c;若不可以返回空数组&#xff0c;若可以直接用一个二重循环遍历一遍即可&#xff0c;时间复杂度 O ( n 2 ) O(n^2) O(n2) class Solution(object):de…

数据结构初阶 · 链式二叉树的部分问题

目录 前言&#xff1a; 1 链式二叉树的创建 2 前序 中序 后序遍历 3 树的节点个数 4 树的高度 5 树的叶子节点个数 6 树的第K层节点个数 前言&#xff1a; 链式二叉树我们在C语言阶段已经实现了&#xff0c;这里介绍的是涉及到的部分问题&#xff0c;比如求树的高度&am…

三、安全工程练习题(CISSP)

1.三、安全工程练习题(CISSP)

找素数第二、三种方法

文章目录 第一种 &#xff1a;使用标签第二种&#xff1a;本质是方法的分装 第一种 &#xff1a;使用标签 没有使用信号量。break和continue作用范围只是最近的循环&#xff0c;无法控制外部循环。 此时使用标签 对外部循环进行操作。 package com.zhang; /* 找素数 第二种方…

【已解决】FileNotFoundError: [Errno 3] No such file or directory: ‘xxx‘

&#x1f60e; 作者介绍&#xff1a;我是程序员行者孙&#xff0c;一个热爱分享技术的制能工人。计算机本硕&#xff0c;人工制能研究生。公众号&#xff1a;AI Sun&#xff0c;视频号&#xff1a;AI-行者Sun &#x1f388; 本文专栏&#xff1a;本文收录于《AI实战中的各种bug…

【C语言】03.分支结构

本文用以介绍分支结构&#xff0c;主要的实现方式为if语句和switch语句。 一、if语句 1.1 if语句 if (表达式)语句表达式为真则执行语句&#xff0c;为假就不执行。在C语言中&#xff0c;0表示假&#xff0c;非0表示真.下图表示if的执行过程&#xff1a; 1.2 else语句 当…

数字孪生概念、数字孪生技术架构、数字孪生应用场景,深度长文学习

一、数字孪生起源与发展 1.1 数字孪生产生背景 数字孪生的概念最初由Grieves教授于2003年在美国密歇根大学的产品全生命周期管理课程上提出&#xff0c;并被定义为三维模型&#xff0c;包括实体产品、虚拟产品以及二者间的连接&#xff0c;如下图所示&#xff1a; 2011年&…

32位和64位的Windows7均不支持UEFI启动方式?试试看!

前言 今天小白突然想起&#xff1a;自己已经接近8年没有安装过32位的Windows系统了&#xff0c;这8年装的上百台电脑都是用的64位Windows。 今天 闲来无事 嗯……应该算是有小伙伴提出了个问题&#xff1a; 这位小伙伴表示&#xff1a;自己无论安装32位还是64位的Windows7都…

OSPF LSA头部详解

LSA概述 LSA是OSPF的本质 , 对于网工来说能否完成OSPF的排错就是基于OSPF的LSDB掌握程度 . 其中1/2类LAS是负责区域内部的 类似于设备的直连路由 . 加上对端的设备信息 3 类LSA是区域间的 指的是Area0和其他Area的区域间关系 , 设计多区域的初衷就是避免大型OSPF环境LSA太多…

14-特殊函数——静态函数、递归函数、函数指针、回调函数、内联函数、变参函数

14-特殊函数——静态函数、递归函数、函数指针、回调函数、内联函数、变参函数 文章目录 14-特殊函数——静态函数、递归函数、函数指针、回调函数、内联函数、变参函数一、静态函数1.1 语法 二、递归函数2.1 示例&#xff1a;输出n个自然数2.2 内存变化 三、函数指针四、指针函…

C++必修:探索C++的内存管理

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;C学习 贝蒂的主页&#xff1a;Betty’s blog 1. C/C的内存分布 我们首先来看一段代码及其相关问题 int globalVar 1; static…

软件测试--Mysql快速入门

文章目录 软件测试-mysql快速入门sql主要划分mysql常用的数据类型sql基本操作常用字段的约束&#xff1a;连接查询mysql内置函数存储过程视图事务索引 软件测试-mysql快速入门 sql主要划分 sql语言主要分为&#xff1a; DQL&#xff1a;数据查询语言&#xff0c;用于对数据进…

作业-day-240607

思维导图 C编程 要求&#xff1a; 搭建一个货币的场景&#xff0c;创建一个名为 RMB 的类&#xff0c;该类具有整型私有成员变量 yuan&#xff08;元&#xff09;、jiao&#xff08;角&#xff09;和 fen&#xff08;分&#xff09;&#xff0c;并且具有以下功能&#xff1a;…

---java 抽象类 和 接口---

抽象类 再面向对对象的语言中&#xff0c;所以的对象都是通过类来描述的&#xff0c;但如果这个类无法准确的描述对象的 话&#xff0c;那么就可以把这个类设置为抽象类。 实例 这里用到abstract修饰&#xff0c;表示这个类或方法是抽象方法 因为会重写motifs里的show方法…

某药监局后缀(第一部分)

声明 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 本文章未经许可禁止转载&#xff…

微服务之熔断器

1、高并发带来的问题 在微服务架构中&#xff0c;我们将业务拆分成一个个的服务&#xff0c;服务与服务之间可以相互调用&#xff0c;但是由于网络原因 或者自身的原因&#xff0c;服务并不能保证服务的100%可用&#xff0c;如果单个服务出现问题&#xff0c;调用这个服务就会…

分别利用线性回归、多项式回归分析工资与年限的关系

一、线性回归&#xff1a; 实验思路&#xff1a; 先分析线性回归的代码&#xff0c;然后结合Salary_dataset.csv内容分析&#xff0c;编写代码。 实验代码&#xff1a; import pandas as pd import numpy as np from sklearn.linear_model import LinearRegression from skle…

【SQLAlChemy】filter过滤条件如何使用?

filter 过滤条件 生成 mock 数据 # 创建 session 对象 session sessionmaker(bindengine)()# 本地生成mock数据 for i in range(6):# 生成随机名字, 长度为4到7个字符name .join(random.choice(string.ascii_letters) for _ in range(random.randint(4, 7)))# 生成随机年龄…

C语言之常用字符串函数总结、使用和模拟实现

文章目录 目录 一、strlen 的使用和模拟实现 二、strcpy 的使用及模拟实现 三、strcat 的使用和模拟实现 四、strcmp 的使用和模拟实现 五、strncpy 的使用和模拟实现 六、strncat 的使用和模拟实现 七、strncmp 的使用和模拟实现 八、strstr 的使用和模拟实现 九、st…