【C++初阶】—— 类和对象 (下)

📝个人主页🌹:EterNity_TiMe_
⏩收录专栏⏪:C++ “ 登神长阶 ”
🌹🌹期待您的关注 🌹🌹

在这里插入图片描述

在这里插入图片描述

类和对象

  • 1. 运算符重载
    • 运算符重载
    • 赋值运算符重载
    • 前置++和后置++重载
  • 2. 成员函数的补充
  • 3. 初始化列表
    • 初始化列表的概念
    • 初始化列表的特征
    • explicit关键字
  • 4. static成员
  • 5. 友元
  • 6. 类的匿名对象
  • 7. 总结


前言:类的6个默认成员函数,我们了解三个,讲完剩下的成员函数,其实类和对象的大致内容已经结束,最后我们在了解一些C++类和对象的剩下的的细节,我们就正式结束类和对象

如果你还对前面三个默认成员函数不太了解,建议先阅读这篇博客
类的成员函数


1. 运算符重载

运算符重载

在一个自定义变量里,如果我们想实现对它的加减乘除,是无法直接使用的,因此C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数

关键字operator 后面接需要重载的运算符符号

函数原型: 返回值类型 operator操作符(参数列表)

举个例子:

// 重载 ==
bool operator==(const Date& d)
{
	return _year = d._year;
	&& _month = d._month;
	&& _day = d._day;
}

注意:

  • 重载操作符必须有一个自定义类型参数
  • 运算符重载定义在类外时不能访问类中的私有成员,因此重载成成员函数
  • 作为类成员函数重载时,成员函数的第一个参数为隐藏的this

赋值运算符重载

1. 关于赋值运算符重载:

  • 参数类型:const T&,传递引用可以提高传参效率
  • 返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
  • 检测是否自己给自己赋值
  • 返回 *this

我们以下例子将使用日期类
例如:

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

	void Print()
	{
		cout << _year << ' ' << _month << ' ' << _day << endl;
	}

	Date& operator=(const Date& d)
	{
		// 检查是否给直接赋值
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}

		return *this;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(2024, 5, 23);
	Date d2 = d1;
	// Date d2;
	// 实际上operator=的调用
	// d2.operator=(d1);
	d1.Print();
	d2.Print();
	return 0;
}

2. 赋值运算符只能重载成类的成员函数不能重载成全局函数

// 假设我们在类外面重载成全局函数
// 注意:在类外是没有 this 指针的
Date& operator=(Date& this, const Date& d)
{
 	if (&this != &d)
 	{
 		this._year = d._year;
		this._month = d._month;
 		this._day = d._day;
 	}
 	return this;
}

我们将写好的代码拿去运行一下,我们发现无法编译
在这里插入图片描述

其实,赋值运算符比较特殊如果不显式实现,编译器会生成一个默认的。如果在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了

在这里插入图片描述


3. 用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝

这里我们要格外注意:
系统默认生成一个默认赋值运算符重载它和之前的拷贝构造一样,发生的是浅拷贝,内置类型成员变量可以直接使用,而自定义类型成员变量需要我们自己调用对应类的赋值运算符重载


前置++和后置++重载

关于前置++和后置++:

  • 前置++:返回+1之后的结果
  • 后置++:是先使用后+1,因此需要返回+1之前的旧值

格式:

  • 因为前置++和后置++符号一样,我们为了要想正确完成重载,C++规定,后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器自动传递
// 前置++
Date& operator++()
{
	_day += 1;
	return *this
}
// 后置++
Date& operator++(int)
{
	Date temp(*this);
	_day += 1;
	return temp;
}
// 前置--
Date& operator--()
{
	_day -= 1;
	return *this
}
// 后置--
Date& operator--(int)
{
	Date temp(*this);
	_day -= 1;
	return temp;
}

最后补充一点,关于运算符重载,并不是所有的运算符都需要重载,而是要根据自定义的类需要重载哪些运算符!

注意以下运算符不能重载:

  • .*
  • ::
  • sizeof
  • ?:
  • .

讲到这里类和对象的大致内容已经结束,剩下两个成员函数,我们简单了解一下


2. 成员函数的补充

const成员

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

例如:

class Date
{
public:
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << "Print()" << endl;
		cout << "year:" << _year << endl;
		cout << "month:" << _month << endl;
		cout << "day:" << _day << endl << endl;
	}
	void Print() const
	{
		cout << "Print()const" << endl;
		cout << "year:" << _year << endl;
		cout << "month:" << _month << endl;
		cout << "day:" << _day << endl << endl;
	}
private:
int _year; // 年
int _month; // 月
int _day; // 日
};
int main()
{
	// 编译器会优先调用符合的函数,如果没有则会根据权限来调用
	// 本质是:权限能缩小,但是不能放大
	// 及非const对象可以调用const成员函数
	// 非const成员函数内可以调用其它的const成员函数
	Date d1(2024,5,23);
	d1.Print();
	const Date d2(2024,5,23);
	d2.Print();
}

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

这两个默认成员函数一般不用重新定义 ,编译器默认会生成!

Date* operator&()
{
	return this ;
}

const Date* operator&()const
{
	return this ;
}

这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需要重载,我们能够修改别人获取的地址


3. 初始化列表

  • 在创建对象时,编译器通过调用构造函数,给对象中各个成员变量一个合适的初始值
  • 对象中有了一个初始值,因此构造函数体中的语句只能将其称为赋初值,而不能称作初始化。因为初始化只能初始化一次,而构造函数体内可以多次赋值

初始化列表的概念

初始化列表: 以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式

Date(int year, int month, int day)
		: _year(year)
		, _month(month)
	{
		_day = day
	}
	// 函数体里面能够放数据
//Date(int year, int month, int day)
//{
//		_year = year;
//		_month = month;
//		_day = day;
//}

初始化列表的特征

使用初始化列表时注意:

  • 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
  • 类中包含以下成员,必须放在初始化列表位置进行初始化:
    • 引用成员变量
    • const成员变量
    • 自定义类型成员(且该类没有默认构造函数时)

例如:

class A
{
public:
	A(int a)
		:_a(a)
	{}
private:
	int _a;
};

class B
{
public:
	B(int a, int ref)
		:_aobj(a)
		,_ref(ref)
		,_n(10)
	{}
private:
	A _aobj; // 没有默认构造函数
	int& _ref; // 引用
	const int _n; // const
};
int main()
{
	B bb(1,2);
	A aa(1);
}

在这里插入图片描述

特征:
1. 尽量使用初始化列表初始化

因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先经过初始化列表初始化

2. 成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关

3. 当用户没有显示传参初始化时,编译器会用用户定义的缺省值

public:
	B(int a)
		:b(a) // b = 1;
	{}
private:
	int b = 1;

explicit关键字

构造函数不仅能构造和初始化对象,对于单个参数或除第一个参数无默认值,其余均有默认值的构造函数,还有隐式类型转换的作用,隐式类型转换是在编程中编译器自动进行的一种类型转换方式

class pxt
{
public:
	explicit pxt(int a = 0)
		:_a(a)
	{
		cout << "pxt(int a)" << endl;
	}
	~pxt()
	{
		cout << "~pxt()" << endl;
	}
private:
	int _a;
};
int main()
{
	pxt a1 = 2024;
	// 用一个整形变量给自定义类型对象赋值
	// 编译器会用2024构造一个无名对象,最后用无名对象给a1对象进行赋值
	// 正常情景是能赋值的,但是explicit修饰构造函数后,会禁止构造函数的隐式转换
	return 0;
}

关键字explicit修饰构造函数,将会禁止构造函数的隐式转换


4. static成员

static成员的概念

概念:

  • 声明为static的类成员称为类的静态成员,
    static修饰的成员变量,称之为静态成员变量,
    static修饰的成员函数,称之为静态成员函数
  • 静态成员变量一定要在类外进行初始化
class pxt
{
public:
	void Print()
	{
		cout << _a << endl;
	}
private:
	// 在类中声明
 	static int _a;
 	//如果是静态成员函数,则没有this指针
};
// 在类外定义
int pxt:: _a = 100;

int main()
{
	pxt A;
	A.Print();
}

static成员的特征

特性:

  • 静态成员为所有类对象所共享,存放在静态区
  • 静态成员变量必须在类外定义,类中只是声明
  • 类静态成员可用 类名::静态成员 或者 对象.静态成员 来访问
  • 静态成员函数没有隐藏的this指针,不能访问任何非静态成员
  • 静态成员也是类的成员,受访问限定符的限制

5. 友元

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

友元分为:

  • 友元函数
  • 友元类

友元函数

如果尝试去重载operator<<,我们发现没办法将operator<<重载成成员函数,因为函数的参数位置不一样,cout的输出流对象和隐含的this指针在抢占第一个参数的位置,重载operator>>同理
在这里插入图片描述
d << cout; -> d.operator<<(&d, cout); 不符合常规调用
因为成员函数第一个参数一定是隐藏的this,所以d必须放在<<的左侧

但是问题来了,如果我们写成全局函数,又无法使用私有的成员变量,这时友元的作用就凸显出来了!

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

例如:

class Date
{
	// 不声明友元,将无法调用私有成员
	friend ostream& operator<<(ostream& _out, const Date& d);
public:
	Date(int year, int month, int day)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
	//void operator<< (ostream& _out)
	//{
	//	_out << _year << " " << _month << " " << _day;
	//}
private:
	int _year; // 年
	int _month; // 月
	int _day; // 日
};

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

int main()
{
	Date d(2024,5,23);
	cout << d << endl;
	// d << cout;
}

关于友元函数有以下几点:

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

友元类

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

友元类的特征:
友元关系是单向的,不具有交换性
友元关系不能传递
如果C是B的友元, B是A的友元,则不能说明C时A的友元
友元关系不能继承,在继承位置再给大家详细介绍

关于友元关系的单向性我举个例子:

class A
{
	friend class B;
public:
	// ......
private:
	int _year;
	int _month;
	int _day;
};
class B
{
public:
	void test(int year, int month, int day)
	{
	// 直接访问A类私有的成员变量
	// 但是A 不能访问B 中私有的成员变量
	_d._year = year;
	_d._month = month;
	_d._day = day;
}
private:
	int good;
	A _d;
};

B能直接访问A类私有的成员变量,但是A 不能访问B 中私有的成员变量


讲到友元类,我们再来介绍一下一个跟友元类有很大关系的内部类
内部类

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

注意:内部类就是外部类的友元类,内部类能访问外部类中的所有成员,反之则不能!

class A
{
public:
	// ......
	A(int year = 2024, int month = 5, int day = 20)
		: _year(year)
		,_month(month)
		,_day(day)
	{}
	class B
	{
	public:
		void test(const A& _d)
		{
			cout << _d._year << " " << _d._month << " " << _d._day << endl;
		}
	};
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	A::B b;
	b.test(A());
}

在这里插入图片描述
内部类的特征

特性:

  • 内部类可以定义在外部类的所有成员
  • 内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名
  • sizeof(外部类)=外部类,和内部类没有任何关系

6. 类的匿名对象

class pxt
{
public:
	pxt(int a = 0)
		:_a(a)
	{
		cout << "pxt(int a)" << endl;
	}
	~pxt()
	{
		cout << "~pxt()" << endl;
	}
private:
	int _a;
};
int main()
{
	// 但是我们可以这么定义匿名对象,匿名对象的特点不用取名字,
	// 但是他的生命周期只有这一行,我们可以看到下一行他就会自动调用析构函数
	// 匿名对象
	pxt();
	// 隐式类型转换
	pxt a1 = 2024;
	return 0;
}

在这里插入图片描述

  • 生命周期只有一行,会自动调用析构函数
  • 匿名对象的特点不用取名字

因此当我们只是想使用类中的某一个函数时,我们能创建匿名对象!


7. 总结

类和对象的所有内容已经了解完毕,类和对象在整个C++上都有举足轻重的作用,大家千万不要忽视,而类和对象的重点在四个成员函数上,下节我将学习C++的内存管理

谢谢大家支持本篇到这里就结束了,祝大家天天开心!
在这里插入图片描述


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

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

相关文章

报错:找不到或无法加载主类 com.example.SpringbootApplication(idea)

OpenJDK 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended 错误: 找不到或无法加载主类 com.example.SpringbootApplication 原因: java.lang.NoClassDefFoundError: com/example/SpringBootAppli…

C++---运算符重载

运算符重载介绍 在类中重新定义运算符&#xff0c;赋予运算符新的功能以适应类的运算&#xff0c;就称为运算符重载。 运算符重载是一种形式的C多态,它使得对象操作更直观,本质上也是属于函数重载。 实际上&#xff0c;我们已经在不知不觉之中使用了运算符重载。例如&#xff…

【JAVA |再谈接口、Object、内部类】Object类中子类重写,Cloneable 接口、比较器、内部类

✨✨谢谢大家捧场&#xff0c;祝屏幕前的小伙伴们每天都有好运相伴左右&#xff0c;一定要天天开心哦&#xff01;✨✨ &#x1f388;&#x1f388;作者主页&#xff1a; &#x1f388;丠丠64-CSDN博客&#x1f388; ✨✨ 帅哥美女们&#xff0c;我们共同加油&#xff01;一起…

python写页面自动截图

from selenium import webdriver def take_screenshot(url, file_path):driver webdriver.Chrome()driver.get(url)driver.save_screenshot(file_path)driver.quit() if __name__ __main__:take_screenshot(http://baidu.com, D:\桌面\wang.png)要安装selenium还要安装google…

React类组件生命周期详解

在React的类组件中&#xff0c;从组件创建到组件被挂载到页面中&#xff0c;这个过程react存在一系列的生命周期函数&#xff0c;最主要的生命周期函数是componentDidMount、componentDidUpdate、componentWillUnmount 生命周期图例如下 1. componentDidMount组件挂载 如果你…

网络安全资源和参考指南

由美国国防部&#xff08;DoD&#xff09;发布的《网络安全资源和参考指南》&#xff0c;旨在为美国政府、商业部门以及美国盟友和伙伴之间的安全合作提供有用的、现成的参考资料。文档涵盖了网络安全规范、最佳实践、政策和标准&#xff0c;这些都是由美国联邦政府、国防部以及…

KubeSphere 社区双周报|2024.05.09-05.23

KubeSphere 社区双周报主要整理展示新增的贡献者名单和证书、新增的讲师证书以及两周内提交过 commit 的贡献者&#xff0c;并对近期重要的 PR 进行解析&#xff0c;同时还包含了线上/线下活动和布道推广等一系列社区动态。 本次双周报涵盖时间为&#xff1a;2024.05.09-05.23…

CCF20230301——田地丈量

CCF20230301——田地丈量 代码如下&#xff1a; #include<bits/stdc.h> using namespace std; int main() {int n,a,b;cin>>n>>a>>b;int x1,x2,y1,y2,x,y,sum0;for(int i0;i<n;i){cin>>x1>>y1>>x2>>y2;xmin(x2,a)-max(x1,…

【科普知识】伺服电机中的内置制动器

在工业自动化和机器人技术快速发展的今天&#xff0c;伺服电机作为核心驱动元件&#xff0c;其性能与功能直接影响整个系统的运行效率与稳定性。 近年来&#xff0c;一体化伺服电机技术不断融合创新&#xff0c;并逐步加入了许多新的硬件和软件的功能&#xff0c;为工业自动化领…

SpringBoot+Vue开发记录(五)-- 数据库设计

我去&#xff0c;时隔这么久又开始了QAQ。主要是还是自己太懒了。 本篇文章的主要内容是数据库设计。 先简单创建个数据库&#xff1a; 这是创建好了的&#xff1a; 一、数据库设计 先就做一个很简单的设计&#xff0c;里面就只有用户和题。 大概就这样&#xff1a; 二、创…

GQL 来了!ISO/IEC 正式发布 GQL 数据库国际标准!

历时四年筹备&#xff0c;超过20个国家的标准和技术专家参与制定&#xff0c;ISO/IEC GQL &#xff08;图查询语言&#xff09;标准于2024年4月12日正式发布&#xff01; 作为国际标准化组织&#xff08;ISO&#xff09;继 1987年 发布SQL后&#xff0c;唯一发布的数据库查询语…

express路由的介绍与使用

一、什么是路由&#xff1f; 官方&#xff1a;路由确定了应用程序如何响应客户端对特定端点的请求 通俗来说&#xff1a;在Web开发中&#xff0c;路由是指根据不同的请求路径和请求方法&#xff0c;将请求分发到相应的处理函数、模块或中间件。简单来说&#xff0c;就是URL到…

【运维心得】双WAN配置的一个误区

目录 双WAN配置及优势 实际案例 解决之道 最后总结 双WAN配置及优势 什么是双WAN配置&#xff0c;这里就不多赘述&#xff0c;简单的说&#xff0c;首先你要有一台支持双WAN口的路由器&#xff0c;目前大多数企业级路由器都具备了这个功能。甚至有些家用路由器也有此类功能…

揭秘:水滴式粉碎机为何如此受欢迎

在粉碎机市场中&#xff0c;水滴式粉碎机以其D特的设计和G效的性能脱颖而出&#xff0c;成为众多用户的选择产品。那么&#xff0c;水滴式粉碎机究竟有何魅力&#xff0c;能够赢得如此广泛的赞誉呢&#xff1f; 首先&#xff0c;水滴式粉碎机的G效性能是其受欢迎的关键因素之一…

【软件设计师】下午题总结-数据流图、数据库、统一建模语言

下午题总结 1 试题一1.1 结构化语言 2 试题二弱实体增加权限增加实体间联系和联系的类型 3 试题三3.1 UML关系例子 3.2 例子&#xff08;2016上半年&#xff09;3.3 设计类分类3.3.1 接口类3.3.2 控制类3.3.3 实体类 3.4 简答题3.4.1 简要说明选择候选类的原则3.4.2 某个类必须…

基于EBAZ4205矿板的图像处理:09基于sobel边缘检测的图像锐化

基于EBAZ4205矿板的图像处理&#xff1a;09基于sobel边缘检测的图像锐化 项目全部文件 随后会上传项目全部文件 先看效果 锐化的有点过头了&#xff0c;不过我也懒得改了&#xff0c;想要改也很简单&#xff0c;无非就是给卷积运算后的结果加个系数&#xff0c;通过改系数调…

Java面试真题日常练习

题目&#xff1a;反转字符串 描述&#xff1a;编写一个函数&#xff0c;输入一个字符串&#xff0c;将其反转并返回结果。 解题思路&#xff1a;可以使用两个指针&#xff0c;一个指向字符串的开头&#xff0c;一个指向字符串的末尾&#xff0c;然后不断交换两个指针所指的字符…

什么是 UUID,uuid

文章目录 一、是什么二、为什么三、怎么用 标题&#xff1a;深入探讨UUID&#xff1a;全球唯一标识符的秘密 一、是什么 在当今数字化时代&#xff0c;唯一标识符&#xff08;UUID&#xff09;在计算机科学领域扮演着重要的角色。UUID是一种用于标识信息的唯一字符串&#xff0…

探索编程乐趣:绘制螺旋图的奇幻之旅

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、引言&#xff1a;编程的魔法世界 二、绘制螺旋图的准备工作 三、代码实战&#xff1a;…

jetcache缓存

1 介绍 是阿里的双极缓存&#xff0c;jvm-->redis-->数据库 文档&#xff1a;jetcache/docs/CN at master alibaba/jetcache GitHub 2 注意事项 使用的实体类一定实现序列化接口定时刷新注解&#xff0c;慎用 它会为每一个key创建一个定时器 &#xff1a;场景为&…