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

目录

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.mfbz.cn/a/471687.html

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

相关文章

最新梨花带雨网页音乐播放器二开优化修复美化版全开源版本源码下载

最新梨花带雨网页音乐播放器二开优化修复美化版全开源版本源码下载 梨花带雨播放器基于thinkphp6开发的XPlayerHTML5网页播放器前台控制面板,支持多音乐平台音乐解析。二开内容:修复播放器接口问题,把接口本地化,但是集成外链播放器接口就不本地化了,我花钱找人写的理解下…

iOS图片占内存大小与什么有关?

1. 问&#xff1a;一张图片所占内存大小跟什么有关&#xff1f; 图片所占内存大小&#xff0c;与图片的宽高有关 我们平时看到的png、jpg、webp这些图片格式&#xff0c;其实都是图片压缩格式。通过对应的算法来优化了大小以节省网络传输与本地保存所需的资源。 但是当我们加…

vant4中如何修改Dialog弹框内容的字体大小

最近在开发一个移动端的需求&#xff0c;用的UI组件库是vant4 简单地总结一下&#xff0c;如何修改Dialog弹框内容的字体大小 我们先看一下Dialog弹框简单的使用 import { showConfirmDialog } from vant;showConfirmDialog({title: 标题,message:如果解决方法是丑陋的&#…

抖音视频无水印下载软件|视频批量提取工具

视频无水印下载软件 随着视频平台上涌现出越来越多精彩的视频内容&#xff0c;很多用户希望能够下载自己喜爱的视频进行保存或分享。为了满足用户需求&#xff0c;我们推出了专业的视频无水印下载软件&#xff0c;让您可以轻松快捷地获取喜欢的视频内容。以下是该软件的安装教…

Sealos 云开发:Laf 出嫁了,与 Sealos 正式结合!

千呼万唤始出来&#xff0c;Laf 云开发最近已正式与 Sealos 融合&#xff0c;入住 Sealos&#xff01;大家可以登录 Sealos 公有云 体验和使用&#xff0c;现在正式介绍一下 Sealos 云开发。 Sealos 云开发是什么&#xff1f; 如图&#xff0c;我们把 Laf 融合到 Sealos 中去了…

MySQL如何用phpMyAdmin创建定时任务事件来执行SQL语句删除_edit_lock和_edit_last?

前面跟大家分享了『WordPress如何批量删除wp_postmeta数据表无用的_edit_lock和_edit_last数据&#xff1f;』和『宝塔面板在计划任务中怎么执行SQL语句删除_edit_lock和_edit_last&#xff1f;』&#xff0c;但是有些站长并不是使用宝塔面板&#xff0c;那么我们如何时间定时删…

AIGC:让生成式AI成为自己的外脑

前言 在数字化浪潮席卷全球的今天&#xff0c;人工智能&#xff08;AI&#xff09;已经渗透到了我们生活的方方面面。其中&#xff0c;生成式AI以其独特的魅力&#xff0c;正逐渐改变我们与世界的交互方式。AIGC&#xff08;人工智能生成内容&#xff09;作为生成式AI的重要应用…

灯塔:CSS笔记(4)

伪类选择器&#xff1a; 1.作用与优势&#xff1a; 1.作用&#xff1a;根据元素在HTML中的结构关系查找元素 2.优势&#xff1a;减少对于HTML中类的依赖&#xff0c;有利于保持代码的整洁 3.场景&#xff1a;常用于查找某父级选择器中的子元素 2.选择器 选择器说明E:first-c…

数据库的基本概念和安装MYSQL数据库

目录 一、数据库的发展 1、文件管理系统的缺点 2、数据库管理系统DBMS的优点 3、数据库管理系统&#xff08;DBMS&#xff09; 3.1DBMS的功能 3.2DBMS的工作模式 4、数据库系统的发展 5、数据库管理系统的架构 6、数据库管理系统分类 二、RDBMS关系型数据库的基本介绍…

软考 网工 每日学习打卡 2024/3/19

学习内容 第8章 网络安全 本章主要讲解网络安全方面的基础知识和应用技术。针对考试应该掌握诸如数据加密、报文认 证、数字签名等基本理论&#xff0c;在此基础上深入理解网络安全协议的工作原理&#xff0c;并能够针对具体的 网络系统设计和实现简单的安全解决方案。 本章共有…

Java学习笔记21——使用JDBC访问MySQL数据库

JDBC&#xff08;Java Database Connectivity&#xff0c;Java数据库连接&#xff09;是应用程序编程借口&#xff08;API&#xff09;&#xff0c;描述了一套访问关系数据库的标准Java类库。可以在程序中使用这些API&#xff0c;连接到关系数据库&#xff0c;执行SQL语句&…

由浅到深认识C语言(7):预处理二进制

该文章Github地址&#xff1a;https://github.com/AntonyCheng/c-notes 在此介绍一下作者开源的SpringBoot项目初始化模板&#xff08;Github仓库地址&#xff1a;https://github.com/AntonyCheng/spring-boot-init-template & CSDN文章地址&#xff1a;https://blog.csdn…

Oracle19C静默安装教程

一、安装前的准备 1、安装Linux操作系统 红帽Linux安装教程 2、配置网络源或者本地源 网络源&#xff1a;网络源配置方法 本地源&#xff1a;本地源配置方法 3、hosts文件配置 配置hostname&#xff1a; hostnamectl set-hostname p19c配置hosts文件&#xff1a; cat &…

从零开始写 Docker(七)---实现 mydocker commit 打包容器成镜像

本文为从零开始写 Docker 系列第七篇&#xff0c;实现类似 docker commit 的功能&#xff0c;把运行状态的容器存储成镜像保存下来。 完整代码见&#xff1a;https://github.com/lixd/mydocker 欢迎 Star 推荐阅读以下文章对 docker 基本实现有一个大致认识&#xff1a; 核心原…

多个线程交替打印ABC

多个线程交替打印ABC package 多个线程交替打印ABC;import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier;/*** Created with IntelliJ IDEA.** Author: AlenXu* Date: 2024/03/20/10:10* Description:*/ public class ThreadLoopP…

数字功放VS模拟功放,选择适合你的音频解决方案

数字功放和模拟功放是音频系统中常用的两种功放技术&#xff0c;适用于不同的音频应用&#xff0c;都具有各自的优势和特点。本文将为您详细介绍数字功放和模拟功放的差异&#xff0c;并帮助您找到适合自己的音频解决方案。 1、数字功放是一种利用数字信号处理技术的功放。它将…

0基础 三个月掌握C语言(13)

数据在内存中的存储 整数在内存中的存储 在讲解操作符时 我们就已经学习了该部分的内容 这里我们回顾一下 整数的二进制表示方法有三种&#xff1a;原码 反码 补码 有符号的整数&#xff08;unsigned&#xff09; 三种表达方式均有符号位和数值位两部分 最高位的一位被当…

【C语言步行梯】C语言实现扫雷游戏(含详细分析)

&#x1f3af;每日努力一点点&#xff0c;技术进步看得见 &#x1f3e0;专栏介绍&#xff1a;【C语言步行梯】专栏用于介绍C语言相关内容&#xff0c;每篇文章将通过图片代码片段网络相关题目的方式编写&#xff0c;欢迎订阅~~ 文章目录 需求分析具体实现主函数体菜单实现游戏实…

AI预测福彩3D第14弹【2024年3月20日预测--新算法重新开始计算第11次测试】

今天继续对第一套算法进行测试。废话不多说了&#xff0c;直接上分析出的图表&#xff0c;再上结果。 最终&#xff0c;经过研判分析&#xff0c;2024年3月20日福彩3D的七码预测结果如下&#xff1a; 百位&#xff1a;7 8 4 3 5 6 9(1换9) 十位&#xff1a;5 4 3 6 1 2 0(07分一…

day14-SpringBoot 原理篇

一、配置优先级 SpringBoot 中支持三种格式的配置文件&#xff1a; 注意事项 虽然 springboot 支持多种格式配置文件&#xff0c;但是在项目开发时&#xff0c;推荐统一使用一种格式的配置 &#xff08;yml 是主流&#xff09;。 配置文件优先级排名&#xff08;从高到低&…
最新文章