C++ —— 类和对象(终)

news2024/11/26 4:46:50

目录

1. 日期类的实现

1.1 前置 ++ 和 后置 ++ 重载

1.2  >> 和 << 的重载

2. const 成员

 3. 取地址及const取地址操作符重载

4. 再谈构造函数 

4.1 构造函数体赋值

4.2 初始化列表

4.3 隐式类型转换

4.4 explict 关键字

5. static 成员

5.1 概念

 5.2 特性

6. 友元

6.1 友元函数

6.2 友元类

7. 内部类

8. 匿名对象 


1. 日期类的实现

 我在上一一篇文章详细写了日期类的计算器,链接如下:日期计算器icon-default.png?t=N7T8https://blog.csdn.net/m0_68617301/article/details/136856490

 主要讲两点:

1.1 前置 ++ 和 后置 ++ 重载

// 前置 ++ ,返回 ++ 之后的内容
Date& Date::operator++()
{
	*this += 1;
	return *this;
}

// 后置 ++, 返回 ++ 之前的内容
Date Date::operator++(int)
{
    // 注意这里是拷贝构造,不是赋值重载
	Date tmp = *this;
	*this += 1;
	return tmp;
}
  • 前置++和后置++都是一元运算符,为了让前置++与后置++形成能正确重载,C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器自动传递。
  • 注意:后置++是先使用后+1,因此需要返回+1之前的旧值,故需在实现时需要先将this保存一份,然后给this+1而tmp是临时对象,因此只能以值的方式返回,不能返回引用

1.2  >> 和 << 的重载

  • 这里的 << 和 >> 的重载不能放在Date类里面,如果放在类里面,第一个参数默认就是this 指针,则 Date 类型的 参数是左操作数,与平时的用法不同。
  • 所以只能定义在类的外面,这个时候如果要访问类里面的 private 参数,可以通过友元的方法。
class Date
{
public:
	
	friend ostream& operator<<(ostream& _out, const Date& d);//友元
	friend istream& operator>>(istream& _in, Date& d);
	
private:
	int _year;
	int _month;
	int _day;
};



ostream& operator<<(ostream& _out, const Date& d)
{
	_out << d._year << "/" << d._month << "/" << d._day;
	return _out;
}

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

2. const 成员

  • 如果定义了 const Date 的对象,再去调用 Print() 函数,会报错;
// 会报错
const Date d2(2022,1,13);
d2.Print();
  • 因为 默认会把 d2 的地址传过去,而 d2 的地址是 const Date* 的,this指针的类型的 Date* 的,会出现权限的放大;
  • 这时候可以把Print()函数后加上const就可以了,例如:
void Print() const
{
    //...
}
  • 这里的加const实际上是再this指针前加了const。
  1. const对象可以调用非const成员函数吗?         不可以,权限放大
  2. 非const对象可以调用const成员函数吗?          可以
  3. const成员函数内可以调用其它的非const成员函数吗?       不可以,权限放大
  4. 非const成员函数内可以调用其它的const成员函数吗?        可以

总的来说,如果是读功能的函数,可以加上const,而写功能的函数不加const。


 3. 取地址及const取地址操作符重载

class A
{
public:
	A* operator&()
	{
		return this;
	}
	const A* operator&()const
	{
		return this;
	}
private:
};

        这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需要重载,比如想让别人获取到指定的内容,可以返回假地址,例如

class A
{
public:
	A* operator&()
	{
		return nullptr;
	}
	const A* operator&()const
	{
		int a = 10;
		return (const A*)&a;
	}
};

4. 再谈构造函数 

4.1 构造函数体赋值

  • 在实例化对象的过程中,编译器会通过调用构造函数对成员变量进行赋值,例如:
class Date
{
public:
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year; // 年
	int _month; // 月
	int _day; // 日
};
  • 构造函数体内容只能称作变量的赋值,不能说是变量的初始化,赋值可以有很多次但是初始化只能有一次,那是在什么时候初始化的呢?

4.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. const 类型成员;
  2. 引用类型;
  3. 自定义类型(没有默认的构造函数)。
  • 所有的成员变量都会通过初始化列表进行定义,所以尽可能的在初始化列表中定义;
  • 对于自定义类型,如果没有默认构造函数(3种),且没有在初始化列表进行初始化,就会报错,因为它必须要进行初始化,而没有对应的构造函数调用。 
class B
{
public:
	/*B(int b = 1)
	{
		this->b = b;
	}*/
    // 如果没有默认的构造函数会报错
    B(int b)
	{
		this->b = b;
	}
private:
	int b;
};


class A
{
public:
	A(int b,int raa)
		:a(1)
		,ra(raa)
		//,bb(b)
	{}

private:
	const int a;
	int& ra;
	B bb;
};

 看一下这段代码:

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

  • 输出结果是随机值,因为在初始化列表中,与你定义的顺序无关,和你在成员变量声明的顺序有关,这里是_a2先声明,_a1再声明,所以在初始化列表中也是先初始化_a2。

4.3 隐式类型转换

  • 先看如下代码
int main()
{
	int i = 10;
	double d = i;
	cout << d << endl;
	return 0;
}

  • 但是下面的一段代码就会报错:
int main()
{
	int i = 10;
	double& d = i;
	cout << d << endl;
	return 0;
}

 

  • 加个const就可以了
int main()
{
	int i = 10;
	const double& d = i;
	cout << d << endl;
	return 0;
}
  •  在类中也有类似的操作,如下的代码就是相当于先把 int 类型的 2 调用构造函数生成A类型的对象,之后再拷贝构造给 a。
class A
{
public :
	A(int a)
		:a(a)
	{}

private:
	int a;
};

int main()
{
	A a = 2;
}
  • 但是通常情况下,现在的编译器会做出相应的优化,例如我想测试编译器是否调用了拷贝构造,但是并没有任何的输出:
class A
{
public :
	A(int a)
		:a(a)
	{}
	A(const A& a)
	{
		cout << "A(const A & a)" << endl;
	}

private:
	int a;
};

int main()
{
	A a = 2;
}

  • 这种情况就是相当于把 构造函数 + 拷贝构造 直接优化成了 构造函数
  • 如下的这种情况也是相同的道理,注意加上const,临时变量具有常性:
class A
{
public :
	A(int a)
		:a(a)
	{}
	A(const A& a)
	{
		cout << "A(const A & a)" << endl;
	}

private:
	int a;
};

int main()
{
    // const 注意
	const A& b = 3;
}
  • 如果构造函数的时候有两个参数,在 C++ 11 后支持这样使用:
class A
{
public:
	A(int a, int b)
		:a(a)
		,b(b)
	{}
	A(const A& a)
	{
		cout << "A(const A & a)" << endl;
	}

private:
	int a;
	int b;
};

int main()
{
	A a = { 1,2 };
}

4.4 explict 关键字

  • 有的时候我们可能不想让构造函数进行隐式类型转换,这个时候可以在构造函数的函数名前加上 explicit 关键字就可以了 。
class A
{
public :
	explicit A(int a)
		:a(a)
	{}
	A(const A& a)
	{
		cout << "A(const A & a)" << endl;
	}

private:
	int a;
};

int main()
{
	//这里就会报错
	const A& b = 3;
    A a = 2;
}

5. static 成员

5.1 概念

  •         声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的成员函数,称之为静态成员函数。静态成员变量一定要在类外进行初始化。
  • 我们可以利用static成员变量计算一共创建了几个对象:
class A
{
public:
	A(int a = 1, int b = 1)
		:a(a)
		,b(b)
	{
		++c;
	}
	A(const A& a)
	{
		cout << "A(const A & a)" << endl;
		++c;
	}
	static int Get()
	{
		return c;
	}

private:
	int a;
	int b;
	static int c;
};

int A::c = 0;

int main()
{
	cout << A::Get() << endl;
	A a1, a2;
	A a3(a1);
	cout << A::Get() << endl;
}

 5.2 特性

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

6. 友元

友元 是一种突破封装的方式

6.1 友元函数

就比如我刚才写的 >> 和 << 的 重载 就利用了友元 :

  •  这里的 << 和 >> 的重载不能放在Date类里面,如果放在类里面,第一个参数默认就是this 指针,则 Date 类型的 参数是左操作数,与平时的用法不同。
  • 所以只能定义在类的外面,这个时候如果要访问类里面的 private 参数,可以通过友元的方法。
class Date
{
public:
	
	friend ostream& operator<<(ostream& _out, const Date& d);//友元
	friend istream& operator>>(istream& _in, Date& d);
	
private:
	int _year;
	int _month;
	int _day;
};



ostream& operator<<(ostream& _out, const Date& d)
{
	_out << d._year << "/" << d._month << "/" << d._day;
	return _out;
}

istream& operator>>(istream& _in, Date& d)
{
	_in >> d._year >> d._month >> d._day;
	return _in;
}
  • 友元函数可访问类的私有和保护成员,但不是类的成员函数
  • 友元函数不能用const修饰
  • 友元函数可以在类定义的任何地方声明,不受类访问限定符限制
  • 一个函数可以是多个类的友元函数
  • 友元函数的调用与普通函数的调用原理相同

6.2 友元类

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

7. 内部类

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

注意:内部类就是外部类的友元类,内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元。

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

8. 匿名对象 

class A
{
public:
	A(int a = 0)
		:_a(a)
	{
		cout << "A(int a)" << endl;
	}
	~A()
	{
		cout << "~A()" << endl;
	}
private:
	int _a;
};
class Solution 
{
public:
	int Sum_Solution(int n) {
		//...
		return n;
	}
};

int main()
{
	A aa1;
	// 不能这么定义对象,因为编译器无法识别下面是一个函数声明,还是对象定义
	//A aa1();
	// 但是我们可以这么定义匿名对象,匿名对象的特点不用取名字,
	// 但是他的生命周期只有这一行,我们可以看到下一行他就会自动调用析构函数
	A();
	A aa2(2);
	Solution().Sum_Solution(10);
	return 0;
}


 

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

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

相关文章

Java 的强引用、弱引用、软引用、虚引用

1、强引用&#xff08;StrongReference&#xff09; 强引用是使用最普遍的引用。如果一个对象具有强引用&#xff0c;那垃圾回收器绝不会回收它。如下&#xff1a; Object onew Object(); // 强引用 当内存空间不足&#xff0c;Java虚拟机宁愿抛出OutOfMemoryError错误&am…

Java多线程实战-CompletableFuture异步编程优化查询接口响应速度

&#x1f3f7;️个人主页&#xff1a;牵着猫散步的鼠鼠 &#x1f3f7;️系列专栏&#xff1a;Java全栈-专栏 &#x1f3f7;️本系列源码仓库&#xff1a;多线程并发编程学习的多个代码片段(github) &#x1f3f7;️个人学习笔记&#xff0c;若有缺误&#xff0c;欢迎评论区指正…

Spring 6.0和SpringBoot 3.0有什么新特性?

Spring在2022年相继推出了Spring Framework 6.0和SpringBoot 3.0&#xff0c;Spring把这次升级称之为新一代框架的开始&#xff0c;下一个10年的新开端 一、问题解析 主要更新内容是以下几个&#xff1a; ● A Java 17 baseline ● Support for Jakarta EE 10 with an EE 9 ba…

永磁同步电机无位置传感器系列(1)——非线性磁链观测器的仿真复现过程

无位置传感器控制&#xff0c;这个方向也是电机控制里面的大热门了。最近在看PLL&#xff0c;中国电机有一篇ESO-PLL获得了23年的高影响力论文&#xff0c;标题如下。因为这篇ESO-PLL需要用到下面这篇英文文献的转子位置观测器&#xff0c;想着就先把这个观测器的文章给复现了。…

【工具】vscode终端打不开

问题 1The terminal process failed to launch: A native exception occurred during launch (forkpty(3) failed.). 参考方案 下面参考链接是针对windows系统上vscode 出现的相同问题的解答 参考链接&#xff1a;https://blog.csdn.net/weixin_40921421/article/details/122…

农业四情监测设备—全面、准确地收集农田环境数据

型号推荐&#xff1a;云境天合TH-Q3】农业四情监测设备是一种高科技的农田监测工具&#xff0c;旨在实时监测和管理农田中的土壤墒情、作物生长、病虫害以及气象条件。这些设备综合运用了传感器、摄像头、气象站等技术手段&#xff0c;能够全面、准确地收集农田环境数据&#x…

机器学习之客户违约预测模型搭建之原理篇

前言 这一章主要介绍机器学习在金融领域一个重要应用&#xff1a;客户违约预测模型的搭建&#xff0c;其所用到原理为机器学习中的决策树模型。通过本章的学习&#xff0c;您能了解在信息时代下金融风险控制的新手段&#xff0c;并对机器学习有一个初步的了解。 1. 机器学习在…

【JavaScript】JavaScript 运算符 ⑤ ( 运算符优先级 )

文章目录 一、JavaScript 运算符优先级1、运算符优先级 概念2、运算符优先级 列举3、运算符示例 一、JavaScript 运算符优先级 1、运算符优先级 概念 JavaScript 的 运算符 是有 " 优先级 " 的 , " 运算符优先级 " 决定 一个表达式中 多个 运算符的 执行顺…

【每日一题】好子数组的最大分数

Tag 【单调栈】【暴力枚举】【数组】【2024-03-19】 题目来源 1793. 好子数组的最大分数 解题思路 本题和 84. 柱状图中最大的矩形 一样&#xff0c;计算的都是最大矩形的面积。只不过多了一个约束&#xff1a;矩形必须包含下标 k。 以下的方法一和方法二是 84. 柱状图中最…

你还在花钱看短剧吗?这些人做短剧推广已经通过短剧赚钱了

短剧分销&#xff0c;简单来说&#xff0c;就是你在抖音、快手、小红书等短视频平台浏览时看到的一半时&#xff0c;指引你去其他平台观看完整版的操作。 而使用“蜂小推”做短剧推广&#xff0c;你也能利用短剧快速致富~ 想要进行短剧分销&#xff0c;你需要经过一系列步骤&a…

23.python标准库之turtle库

一、窗体函数 turtle.setup(width, height, startx, starty) width:窗口宽度 height:窗口高度 startx:窗口与屏幕左侧距离&#xff08;单位象素&#xff09; starty:窗口与屏幕顶部距离&#xff08;单位象素&#xff09; 二、画笔状态函数 三、画笔运动函数

理清大数据技术与架构

大数据并不是一个系统软件&#xff0c;更不是一个单一的软件&#xff0c;它实际上是一种技术体系、一种数据处理方法&#xff0c;甚至可以说是一个服务平台。在这个技术体系中&#xff0c;涵盖了许多不同的部件&#xff0c;比如Hadoop服务平台。这一服务平台可以根据具体情况自…

Mybatis-Plus通用枚举介绍和使用

本文使用的是 Mybatis-Plus 从 3.4.2 版本来实现通用枚举配置&#xff0c;不同的版本&#xff0c;通用枚举配置会不一样&#xff0c;而且从Mybatis-Plus 从 3.5.2 版本开始只需使用 EnumValue 注解枚举属性&#xff0c;就可以实现一系列功能&#xff0c;而不需要其他配置。 这…

【python】python结合js逆向,让有道翻译成为你的翻译官,实现本地免费实时翻译

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN新星创作者等等。 🏆《博客》:Python全栈,前后端开发,人工智能,js逆向,A…

Spring Cloud 整合 GateWay

目录 第一章 微服务架构图 第二章 Spring Cloud整合Nacos集群 第三章 Spring Cloud GateWay 第四章 Spring Cloud Alibaba 整合Sentinel 第五章 Spring Cloud Alibaba 整合SkyWalking链路跟踪 第六章 Spring Cloud Alibaba 整合Seata分布式事务 第七章 Spring Cloud 集成Auth用…

Jenkins通知目标服务器拉取Harbor镜像部署

1.告诉目标服务器拉取哪个镜像 2.判断当前有没有正在运行此容器&#xff0c;有就删除 3.接着查看拉取的镜像目标服务器上是否已存在&#xff0c;有就删除 4.拉取Harbor镜像 5.运行容器 目标服务器编写脚本 创建个部署脚本 vim deploy.sh告诉目标服务器Harbor地址、仓库、镜像…

零基础学python:5、循环语句的使用

循环 目标 程序的三大流程while 循环基本使用break 和 continuewhile 循环嵌套01. 程序的三大流程 在程序开发中,一共有三种流程方式: 顺序 —— 从上向下,顺序执行代码分支 —— 根据条件判断,决定执行代码的 分支循环 —— 让 特定代码 重复 执行02. while 循环基本使用…

基于卷积神经网络实现手写数字识别

基于卷积神经网络实现手写数字识别 基于卷积神经网络实现手写数字识别。具体过程如下&#xff1a; &#xff08;1&#xff09; 定义ConvNet结构类及其前向传播方式 &#xff08;2&#xff09; 设置超参数以及导入相关的包。 &#xff08;3&#xff09; 定义训练网络函数和绘…

R语言实现多要素偏相关分析

偏相关分析是指当两个变量同时与第三个变量相关时&#xff0c;将第三个变量的影响剔除&#xff0c;只分析另外两个变量之间相关程度的过程&#xff0c;判定指标是相关系数的R值。 在GIS中&#xff0c;偏相关分析也十分常见&#xff0c;我们经常需要分析某一个指数与相关环境参…

基于Java中的SSM框架实现快餐店线上点餐系统项目【项目源码+论文说明】

基于Java中的SSM框架实现快餐店线上点餐系统演示 摘要 随着计算机互联网的高速发展。餐饮业的发展也加入了电子商务团队。各种网上点餐系统纷纷涌现&#xff0c;不仅增加了商户的销售量和营业额&#xff0c;而且为买家提供了极大的方便&#xff0c;足不出户&#xff0c;就能订…