我与C++的爱恋:类和对象(四)


外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

🔥个人主页guoguoqiang. 🔥专栏我与C++的爱恋

Alt

朋友们大家好!本篇是类和对象的最后一个部分。

一、static成员

声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的成员函数,称之为静态成员函数。静态成员变量一定要在类外进行初始化
统计A类中创建了多少个对象

class A
{
public:
	A()
	{}

	A(const A& a)
	{

	}
private:
};
A Func()
{
	A aa;
	return aa;
}
int main()
{
	A aa1;
	A aa2;
	Func();
	return 0;
}

我们可以想象一下用了多少次构造函数就创建了多少对象,我们可以通过全局变量来计数

int count=0;
class A
{
public:
	A()
	{
	++count;
	}

	A(const A& a)
	{
	++count
	}
private:
};
A Func()
{
	A aa;
	return aa;
}
int main()
{
	A aa1;
	A aa2;
	Func();
	cout<<count<<endl'
	return 0;
}

但这个count是全局变量可能会被随意修改,能不能把他封装到类中呢?

class A
{
public:
	A()
	{
	++count;
	}

	A(const A& a)
	{
	++count
	}
private:
int count=0;
};

但是这里会是临时拷贝可能不是同一个count;
这里需要设置为静态变量

private:
static int count=0;

静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区。所以需要在类外定义

class A
{
public:
	A()
	{
	++count;
	}

	A(const A& a)
	{
	++count
	}
private:
static int count=0;
};
int A::count=0;

这个count则受到类的限制,无法随意访问,如果想访问count,有两种办法:

方法一,将count改为公有,但是破坏了封装性,不建议
方法二,get函数

class A
{
public:
A() { 
	++_scount;
}
A(const A & t) {
	++_scount; 
}
~A() {
	--_scount;
}
static int GetACount() { //get
	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;
}
class A
{
public:
	A()
	{
	++count;
	}

	A(const A& a)
	{
	++count
	}
	static int Getcount(){
		return count;
	}
private:
static int count=0;
};

静态成员函数在类中有特殊的作用和行为.
静态成员函数不能调用非静态成员函数,
非静态成员函数可以调用静态成员函数。
静态成员函数通常用于提供一些与类的任何特定实例无关的功能,或者访问静态成员变量,而不依赖于类的对象。在设计类时,如果某个函数的行为不需要依赖于对象的状态,那么就应该将其声明为静态的

1特性

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

二、友元

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

友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在 类的内部声明,声明时需要加friend关键字。

1.重载<<和>>

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

class Date
{
public:
Date(int year, int month, int day)
: _year(year)
, _month(month)
, _day(day)
{}
// d1 << cout; -> d1.operator<<(&d1, cout); 不符合常规调用
// 因为成员函数第一个参数一定是隐藏的this,所以d1必须放在<<的左侧
ostream& operator<<(ostream& _cout)
{
_cout << _year << "-" << _month << "-" << _day << endl;
return _cout;
}
private:
int _year;
int _month;
int _day;
};

我们之前重载时会不符合常规调用。
作为成员函数重载,this指针占据了第一个参数,意味着Date必须是左操作数
所以这个函数只能写成全局函数,这里访问不了私有先置为公有

class Date
{
public:
	Date(int year, int month, int day)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
	
//private:
	int _year;
	int _month;
	int _day;
};
void operator<<(ostream& out,const Date &d)
{
	out << d._year << "-" << d._month << "-" <<d._day;
}
int main()
{
	Date d1(2024, 4, 22);
	cout << d1;
	return 0;
}

在这里插入图片描述
这下可以访问了,但不能连续赋值。

	Date d1(2024, 4, 22);
	Date d2(2024, 4, 20);
	cout << d1<<d2;

在这里插入图片描述
在这里我们可以理解为cout是从左往右进行的 cout<<d1 返回cout,返回后再继续流输出。

ostream& operator<<(ostream& out,const Date &d)
{
	out << d._year << "-" << d._month << "-" <<d._day;
}

同理可以写出流提取:

istream& operator>>(istream& _cin, Date& d)
{
 _cin >> d._year;
 _cin >> d._month;
 _cin >> d._day;
 return _cin;
}

2.友元函数

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

友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在 类的内部声明,声明时需要加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;
};

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

3.友元类

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

class Time
{
	friend class Date;//日期类中可以直接访问Time中的私有成员变量,但是Time中不能访问Date的私有
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类中直接
访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。
2.友元关系不能传递。如果C是B的友元, B是A的友元,则不能说明C时A的友元。
3.友元关系不能继承。

三、内部类(内部类是外部类的私有)

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

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

class A
{
private:
	static int k;
	int h;
public:
	class B // B天生就是A的友元
	{
	public:
		void fun(const A& a)
		{
			cout << k << endl;//OK
			cout << a.h << endl;//OK
		}
	};
};
int A::k = 1;
int main()
{
	A::B b;
	b.fun(A());

	return 0;
}

B可以访问A的所有成员
特性:

1.内部类可以定义在外部类的public、protected、private都是可以的。
2.注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名。
3.sizeof(外部类)=外部类,和内部类没有任何关系

	A::B b;

B这个类受到A类的类域的限制

四、匿名对象

class A
{
public:
	A(int a = 0)
		:_a(a)
	{
		cout << "A(int a)" << endl;
	}
	~A()
	{
		cout << "~A()" << endl;
	}
private:
	int _a;
};

有名对象:
A aa1 A aa2(2)
匿名对象:
A() A(11)

class A
{
public:
    A(int a = 0)
        :_a(a)
    {
        cout << "A(int a)" << endl;
    }
    ~A()
    {
        cout << "~A()" << endl;
    }
private:
    int _a;
};

int main()
{
	A aa1(1);
    A();
// 我们可以这么定义匿名对象,匿名对象的特点不用取名字,
// 但是他的生命周期只有这一行,下一行他就会自动调用析构函数
    return 0;
}

在这里插入图片描述
其中第二,三行是A()的构造函数和析构函数。

class Solution {
public:
 int Sum_Solution(int n) {
 //...
 return n;
 }
};
int main()
{
	Solution().Sum_Solution(10);
	return 0;
}

匿名对象在这样场景下就很好用,当我需要一个临时对象去调用其成员函数,但又不想为这个临时使用的对象创建一个具体的变量名,这样使用就很方便。

拷贝对象时的一些编译器优化:
隐式类型,连续构造+拷贝构造->优化为直接构造
一个表达式中,连续构造+拷贝构造->优化为一个构造
一个表达式中,连续拷贝构造+拷贝构造->优化为一个拷贝构造

本节内容到此结束!感谢大家观看!!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[阅读笔记29][AgentStudio]A Toolkit for Building General Virtual Agents

这篇论文是24年3月提交的&#xff0c;提出了一个用于agent开发的全流程工具包。 作者提到目前agent开发主要有两个阻碍&#xff0c;一个是缺乏软件基础&#xff0c;另一个是缺乏在真实世界场景中进行评估。针对这两个阻碍&#xff0c;作者涉及了一个开发工具包&#xff0c;包括…

使用立创EDA打开JSON格式的PCB及原理图

一、将PCB和原理图放同一文件夹 并打包成.zip文件 二、打开嘉立创EDA并导入.zip文件 文件 -> 导入 -> 嘉立创EDA标准版/专业版 三、选择.zip文件并选择 “导入文件并提取库” 四、自定义工程路径 完成导入并转换为.eprj文件 五、视频教学 bilibili_使用立创EDA打开JSO…

NLP预训练模型-GPT-3

ChatGPT GPT-3是OpenAI开发的一个自然语言处理&#xff08;NLP&#xff09;预训练模型。GPT代表“生成式预训练变换器”&#xff08;Generative Pretrained Transformer&#xff09;。GPT-3是GPT系列的第三代模型&#xff0c;是一种采用了深度学习技术的强大语言模型&#xff…

驱动开发-windows驱动设计目标

驱动程序和应用程序不一样的&#xff0c;由于其直接运行于windows r0级&#xff0c;故对于开发有更多和更严格的标准&#xff0c;一般会有以下一些常见的设计目标: 安全性、可移植性、可配置性、 可被中断、多处理器安全、可重用 IRP、 支持异步 I/O这些是基本目标。 1. 安全…

【Numpy】对于 Numpy 中 Axis 的理解

文章目录 前言理解轴的两个角度在维度变化方向上计算降维 示例剖析写在最后 前言 Numpy 是 Python 中一个常用科学计算库&#xff0c;常用来表示向量、矩阵以及多维度数组。在 Numpy 中多对某一个维度&#xff08;轴&#xff09;进行相应的操作&#xff0c;这一点经常出错。今…

再论图像变化和频率的关系。

我之前是做了一些探讨&#xff0c;但是没说清楚&#xff0c;现在再看这个问题。 我先提出这个问题。 以以为点列为例&#xff0c;先写成傅里叶级数的形式&#xff0c;不过这里不是三角函数形式&#xff0c;而是指数形式&#xff0c;是一样的。 对f(n)求导&#xff0c;就可以观…

【大语言模型LLM】-使用大语言模型搭建点餐机器人

关于作者 行业&#xff1a;人工智能训练师/LLM 学者/LLM微调乙方PM发展&#xff1a;大模型微调/增强检索RAG分享国内大模型前沿动态&#xff0c;共同成长&#xff0c;欢迎关注交流… 大语言模型LLM基础-系列文章 【大语言模型LLM】-大语言模型如何编写Prompt?【大语言模型LL…

C语言—字符指针,指针数组和数组指针详解

字符指针 在指针的类型中我们知道有一种指针类型为字符指针 char* ; int main() {char ch w;char *pc &ch;*pc w;return 0; }还有一种使用方式如下&#xff1a; int main() {const char* pstr "hello world.";//这里是把一个字符串放到pstr指针变量里了吗…

chrome浏览器查看css样式

样式的查看 1.匹配器为灰色文本&#xff1a; 表示非当前选择器 2.样式有划线标识&#xff1a;CSS属性无效或未知 / 属性值无效 / 被其他属性覆盖的属性 3.属性以浅色文本显示且有感叹号提示&#xff1a;属性虽然有效&#xff0c;但由于CSS逻辑而没有任何影响 转自&#xff1a;…

笔试狂刷系列--Day1

大家好,我是LvZi,今天开启新的章节笔试狂刷系列 一.两个数组的交集 1. 题⽬链接: 两个数组的交集 思路分析: 查找两个数组的公共元素,一开始可能想到使用Set,先遍历第一个数组,存储nums1中所有的元素,接着遍历nums2中的所有元素,判断是否在Set之中,但是发现在遍历第二个数组…

神经网络中的神经元和激活函数介绍

文章目录 1、什么是人工神经网络 2、什么是神经元 3、什么是激活函数 线性激活函数 Sigmoid激活函数 双曲正切激活函数 修正线性单元&#xff08;ReLU&#xff09;激活函数 Leaky ReLU激活函数 Softmax激活函数 1、什么是人工神经网络 神经网络能够利用多层神经元学习复杂的模…

使用docker打包当前服务器的neo4j环境

Docker 是一个开源的应用容器引擎,它允许开发者将应用程序及其依赖打包到一个可移植的容器中,这样应用程序就可以在任何支持Docker的平台上运行,而无需担心环境差异。 当运行一个Docker容器时,它会加载一个镜像并运行它。Docker在容器内部创建一个隔离的环境,这个环境被称…

Redis学习-Redis的九种数据结构

String &#xff08;字符串&#xff09; 虽然redis是用C语言编写&#xff0c;但是redis中的string是redis自己实现的字符串结构&#xff0c;叫Simple Dynamic String简称&#xff08;SDS&#xff09;&#xff0c;因为redis做为中间件会接受不同语言编写的程序传过来的字符串&a…

Oracle Hint 语法详解

什么是Hint Hint 是 Oracle 提供的一种 SQL 语法&#xff0c;它允许用户在 SQL 语句中插入相关的语法&#xff0c;从而影响 SQL 的执行方式。 因为 Hint 的特殊作用&#xff0c;所以对于开发人员不应该在代码中使用它&#xff0c;Hint 更像是 Oracle 提供给 DBA 用来分析诊断问…

Python中pyside2出现的pyside2 qt platform plugin could be in错误及其解决方法

系统平台&#xff1a;Win10 64bit python版本&#xff1a; python 3.8 使用pip install pyside2安装 pyside2 这是找不到QT平台的插件&#xff0c;这是环境变量QT_QPA_PLATFORM_PLUGIN_PATH出现错误 具体解决方法&#xff1a; 我们可以在每一段程序开始之前设定环境变量&…

pytorch与深度学习

ChatGPT PyTorch是一个由Facebook AI Research Team开发的开源深度学习库&#xff0c;它提供了一个灵活的环境和丰富的API&#xff0c;用于快速且方便地构建、训练和部署深度学习模型。PyTorch在科学界和工业界都收到了广泛的使用&#xff0c;其中包括了学术研究、小型项目和大…

第50篇:算法的硬件实现<一>

Q&#xff1a;本期我们来开始介绍如何使用算法状态机&#xff08;ASM&#xff09;图在硬件开发板上实现算法。 A&#xff1a;算法状态机 (Algorithmic State Machine&#xff0c;ASM) 图是描述数字系统控制单元的工作流程图&#xff0c;主要用来描述控制单元的时序操作特性&am…

功能测试用例设计思路

我们为什么要写好一份测试用例呢&#xff1f;测试同学应该都知道测试用例的重要性&#xff0c;测试用例就是我们测试的依据&#xff0c;也是测试过程中不能缺少的测试文档。 一、用例编写规范目的&#xff1a; 1、提高测试用例的可读性&#xff0c;可执行性、合理性。 2、测试…

Spring(下)

接上篇&#xff0c;从第八个问题讲起 八.Spring工厂创建复杂对象 1.什么是复杂对象 简单对象就是可以直接new出来的&#xff0c;也就是直接调用构造方法创建 所以复杂对象就是不能直接通过调用构造方法创建。就比如JDBC中的Connection 2.三种方法 &#xff08;1&#xff…

4月阿里offer被毁,我该怎么进字节?

在校招求职的浪潮中&#xff0c;有些故事总是让人唏嘘不已。比如最近在社交平台上广泛讨论的一个话题&#xff1a;“4月阿里offer被毁&#xff0c;我该怎么进字节&#xff1f;”这不仅反映了当下职场的变动性&#xff0c;也映射了求职者在面对突如其来的变故时的无助与挣扎。 …