【C++】透析类和对象(下)

news2024/9/20 18:26:23

有不懂的可以翻阅我之前文章!

                     个人主页:CSDN_小八哥向前冲

                      所属专栏:CSDN_C++入门


目录

拷贝构造函数

运算符重载

赋值运算符重载

取地址运算符重载

const成员函数

取地址重载

再探构造函数

初始化列表

类型转换

static成员

友元

内部类

匿名对象

对象拷贝时的编译器优化

日期类的实现

Date.h文件

Data.cpp文件


拷贝构造函数

拷贝构造的特点:

  1. 拷⻉构造函数是构造函数的⼀个重载。
  2. . 拷⻉构造函数的参数只有⼀个且必须是类类型对象的引⽤,使⽤传值⽅式编译器直接报错,因为语 法逻辑上会引发⽆穷递归调⽤
  3. C++规定⾃定义类型对象进⾏拷⻉⾏为必须调⽤拷⻉构造,所以这⾥⾃定义类型传值传参和传值返 回都会调⽤拷⻉构造完成。
  4. 若未显式定义拷⻉构造,编译器会⽣成⾃动⽣成拷⻉构造函数。⾃动⽣成的拷⻉构造对内置类型成员变量会完成值拷⻉/浅拷⻉(⼀个字节⼀个字节的拷⻉),对⾃定义类型成员变量会调⽤他的拷⻉构造。
  5. 传值返回会产⽣⼀个临时对象调⽤拷⻉构造,传值引⽤返回,返回的是返回对象的别名(引⽤),没 有产⽣拷⻉。但是如果返回对象是⼀个当前函数局部域的局部对象,函数结束就销毁了,那么使⽤ 引⽤返回是有问题的,这时的引⽤相当于⼀个野引⽤,类似⼀个野指针⼀样。传引⽤返回可以减少 拷⻉,但是⼀定要确保返回对象,在当前函数结束后还在,才能⽤引⽤返回。

拷贝构造参数必须用引用,否则编译错误,因为会引发无穷递归的拷贝构造!

如图:

运算符重载

运算符重载特点:

  1. 运算符重载是具有特名字的函数,他的名字是由operator和后⾯要定义的运算符共同构成。和其他 函数⼀样,它也具有其返回类型和参数列表以及函数体。
  2. 重载运算符函数的参数个数和该运算符作⽤的运算对象数量⼀样多。⼀元运算符有⼀个参数,⼆元 运算符有两个参数,⼆元运算符的左侧运算对象传给第⼀个参数,右侧运算对象传给第⼆个参数。
  3. 如果⼀个重载运算符函数是成员函数,则它的第⼀个运算对象默认传给隐式的this指针,因此运算 符重载作为成员函数时,参数⽐运算对象少⼀个。
  4.    .*    : :    sizeof   ?:     . ) 注意以上5个运算符不能重载。
  5. 重载++运算符时,有前置++和后置++,运算符重载函数名都是operator++,⽆法很好的区分。 C++规定,后置++重载时,增加⼀个int形参,跟前置++构成函数重载,⽅便区分。
  6. 重载>>和<<时,需要重载为全局函数,因为重载为成员函数,this指针默认抢占了第⼀个形参位置,第⼀个形参位置是左侧运算对象,调⽤时就变成了对象<<cout,不符合使⽤习惯和可读性。重载为全局函数把ostream/istream放到第⼀个形参位置就可以了,第⼆个形参位置当类类型对象

前置++和后置++的区别:

赋值运算符重载

赋值运算符重载注意事件:

  1. 赋值运算符重载是⼀个运算符重载,规定必须重载为成员函数。赋值运算重载的参数建议写成 const 当前类类型引⽤,否则会传值传参会有拷⻉
  2. 有返回值,且建议写成当前类类型引⽤,引⽤返回可以提⾼效率,有返回值⽬的是为了⽀持连续赋 值场景。
  3. 没有显式实现时,编译器会⾃动⽣成⼀个默认赋值运算符重载,默认赋值运算符重载⾏为跟默认构 造函数类似,对内置类型成员变量会完成值拷⻉/浅拷⻉(⼀个字节⼀个字节的拷⻉),对⾃定义类型 成员变量会调⽤他的拷⻉构造。

参数和返回值写成引用的形式的原因:

可以提高效率,避免拷贝,因为传值传参会有拷贝!

注意:

  1. 当只是简单的赋值运算(浅拷贝),那么可以不写这个赋值重载,编译器自身的就能解决!
  2. 当需要深层次的拷贝(涉及指针指向空间的这种),那么就要自己实现赋值函数!

还有一种情况赋值重载会和拷贝构造混淆:

取地址运算符重载

const成员函数

特点:

  1. 将const修饰的成员函数称之为const成员函数,const修饰成员函数放到成员函数参数列表的后 ⾯
  2. const实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进⾏修改。

不管变量还是函数利用const 修饰都有一个共同的特点:不变。

值得我们注意的是——不能权限扩大

取地址重载

取地址运算符重载分为普通取地址运算符重载和const取地址运算符重载,⼀般这两个函数编译器⾃动⽣成的就可以够我们⽤了,不需要去显⽰实现。除⾮⼀些很特殊的场景,⽐如我们不想让别⼈取到当 前类对象的地址,就可以⾃⼰实现⼀份,胡乱返回⼀个地址。

这个不怎么常用,我们不做过多的介绍。

再探构造函数

初始化列表

内容:

  1. 之前我们实现构造函数时,初始化成员变量主要使⽤函数体内赋值,构造函数初始化还有⼀种⽅式,就是初始化列表,初始化列表的使⽤⽅式是以⼀个冒号开始,接着是⼀个以逗号分隔的数据成 员列表,每个"成员变量"后⾯跟⼀个放在括号中的初始值或表达式。
  2. 每个成员变量在初始化列表中只能出现⼀次,语法理解上初始化列表可以认为是每个成员变量定义 初始化的地⽅。
  3. 引⽤成员变量,const成员变量,没有默认构造的类类型变量,必须放在初始化列表位置进⾏初始化,否则会编译报错。
  4. C++11⽀持在成员变量声明的位置给缺省值,这个缺省值主要是给没有显⽰在初始化列表初始化的 成员使⽤的。
  5. 尽量使⽤初始化列表初始化,因为那些你不在初始化列表初始化的成员也会⾛初始化列表,如果这 个成员在声明位置给了缺省值,初始化列表会⽤这个缺省值初始化。如果你没有给缺省值,对于没 有显⽰在初始化列表初始化的内置类型成员是否初始化取决于编译器,C++并没有规定。对于没有 显⽰在初始化列表初始化的⾃定义类型成员会调⽤这个成员类型的默认构造函数,如果没有默认构 造会编译错误。
  6. 初始化列表中按照成员变量在类中声明顺序进⾏初始化,跟成员在初始化列表出现的的先后顺序⽆ 关。建议声明顺序和初始化列表顺序保持⼀致。

第三点的原因:

因为这三种类型变量定义时必须要初始化,否则就报错!

初始化列表的使用形式:

初始化列表初始化数据规则:

我们来看个题目:

类型转换

  1. C++⽀持内置类型隐式类型转换为类类型对象,需要有相关内置类型为参数的构造函数。
  2. 构造函数前⾯加explicit就不再⽀持隐式类型转换。

static成员

内容:

  1. ⽤static修饰的成员变量,称之为静态成员变量静态成员变量⼀定要在类外进⾏初始化。(可以在类里面声明)。
  2. 静态成员变量为所有类对象所共享,不属于某个具体的对象,不存在对象中,存放在静态区
  3. ⽤static修饰的成员函数,称之为静态成员函数,静态成员函数没有this指针
  4. 静态成员函数中可以访问其他的静态成员,但是不能访问⾮静态的,因为没有this指针
  5. ⾮静态的成员函数,可以访问任意的静态成员变量和静态成员函数。
  6. 突破类域就可以访问静态成员,可以通过类名::静态成员或者对象.静态成员来访问静态成员变量 和静态成员函数。
  7. 静态成员也是类的成员,受public、protected、private访问限定符的限制
  8. 静态成员变量不能在声明位置给缺省值初始化因为缺省值是个构造函数初始化列表的,静态成员 变量不属于某个对象,不⾛构造函数初始化列表

看题:

友元

  1. 友元提供了⼀种突破类访问限定符封装的⽅式,友元分为:友元函数和友元类,在函数声明或者类 声明的前⾯加friend,并且把友元声明放到⼀个类的⾥⾯。
  2. 外部友元函数可访问类的私有和保护成员,友元函数仅仅是⼀种声明,他不是类的成员函数
  3. 友元函数可以在类定义的任何地⽅声明,不受类访问限定符限制。
  4. ⼀个函数可以是多个类的友元函数。
  5. 友元类中的成员函数都可以是另⼀个类的友元函数,都可以访问另⼀个类中的私有和保护成员。
  6. 友元类的关系是单向的,不具有交换性,⽐如A类是B类的友元,但是B类不是A类的友元。
  7. 友元类关系不能传递,如果A是B的友元,B是C的友元,但是A不是B的友元。
  8. 有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多⽤

内部类

  1. 如果⼀个类定义在另⼀个类的内部,这个内部类就叫做内部类。内部类是⼀个独⽴的类,跟定义在 全局相⽐,他只是受外部类类域限制和访问限定符限制,所以外部类定义的对象中不包含内部类。
  2. 内部类默认是外部类的友元类。
  3. 内部类本质也是⼀种封装,当A类跟B类紧密关联,A类实现出来主要就是给B类使⽤,那么可以考 虑把A类设计为B的内部类,如果放到private/protected位置,那么A类就是B类的专属内部类,其 他地⽅都⽤不了

匿名对象

  • ⽤类型(实参)定义出来的对象叫做匿名对象,相⽐之前我们定义的类型对象名(实参)定义出来的 叫有名对象。
  • 匿名对象⽣命周期只在当前⼀⾏,⼀般临时定义⼀个对象当前⽤⼀下即可,就可以定义匿名对象。

对象拷贝时的编译器优化

  1. 现代编译器会为了尽可能提⾼程序的效率,在不影响正确性的情况下会尽可能减少⼀些传参和传参过程中可以省略的拷⻉。
  2. 如何优化C++标准并没有严格规定,各个编译器会根据情况⾃⾏处理。当前主流的相对新⼀点的编 译器对于连续⼀个表达式步骤中的连续拷⻉会进⾏合并优化,有些更新更"激进"的编译还会进⾏跨 ⾏跨表达式的合并优化。

我们知道传值传参会进行拷贝构造,如果很多地方我们都不用引用的话,就会出现大量的拷贝构造,这时候编译器会优化,直接跳过临时对象这一步骤,直接构造!

日期类的实现

那么我们类和对象就已经全部搞定!

我们来实现一个日期类巩固一下!比较简单,我们就不做过多解释,直接上代码!

Date.h文件

#include<iostream>
using namespace std;
#include<assert.h>

namespace ywc
{
	class Date
	{
	public:
		friend istream& operator>>(istream& in, Date& d);
		friend ostream& operator<<(ostream& out, Date& d);
		Date(int year=1,int month=1,int day=1)
			:_year(year)
			,_month(month)
			,_day(day)
		{
			if (!CheckDate())
			{
				cout << "日期非法" << endl;
				cout << *this;
			}
		}
		int GetMonthDay()
		{
			assert(_month < 13 && _month > 0);
			int MonthDay[] = { -1,31,28,31,30,31,30,31,31,30,31,30,31 };
			if (_month == 2 && ((_year % 4 == 0 && _year % 100 != 0) 
				|| (_year % 400 == 0)))
			{
				return 29;
			}
			return MonthDay[_month];
		}
		bool CheckDate()
		{
			if (_year > 0)
			{
				if (_month > 0 && _month < 13)
				{
					if (_day > 0 && _day <= GetMonthDay())
					{
						return true;
					}
				}
			}
			return false;
		}
		~Date()
		{
			_year = _month = _day = 0;
		}
		//日期+/-天数
		Date operator+(int day);
		Date operator-(int day);
		Date& operator+=(int day);
		Date& operator-=(int day);
		//前置++
		Date operator++();
		Date& operator++(int);
		//日期-日期
		int operator-(Date& d);
		//日期比较大小
		bool operator==(Date& d);
		bool operator<(Date& d);
		bool operator>(Date& d);
		bool operator<=(Date& d);
		bool operator>=(Date& d);
		bool operator!=(Date& d);
	private:
		int _year;
		int _month;
		int _day;
	};
	//全局输入输出函数
	istream& operator>>(istream& in, Date& d);
	ostream& operator<<(ostream& out, Date& d);
}

Data.cpp文件

#include"Date.h"

namespace ywc
{
	Date& Date::operator+=(int day)
	{
		if (day < 0)
		{
			return *this -= (-day);
		}
		_day += day;
		while (_day > GetMonthDay())
		{
			_day -= GetMonthDay();
			_month++;
			if (_month == 13)
			{
				_month = 1;
				_year++;
			}
		}
		return *this;
	}
	Date Date::operator+(int day)
	{
		Date tmp = *this;
		return tmp += day;
	}
	Date& Date::operator-=(int day)
	{
		if (day < 0)
		{
			return *this += (-day);
		}
		_day -= day;
		while (_day <= 0)
		{
			_month--;
			if (_month == 0)
			{
				_year--;
				_month = 12;
			}
			_day += GetMonthDay();
		}
		return *this;
	}
	Date Date::operator-(int day)
	{
		Date tmp = *this;
		return tmp -= day;
	}
	int Date::operator-(Date& d)
	{
		int flag = 1;
		Date max =*this, min = d;
		if (max < min)
		{
			max = d;
			min = *this;
			flag = -1;
		}
		int n = 0;
		while (min!=max)
		{
			++min;
			++n;
		}
		return n*flag;
	}
	Date Date::operator++()
	{
		Date tmp = *this;
		*this += 1;
		return tmp;
	}
	Date& Date::operator++(int)
	{
		return (*this += 1);
	}
	bool Date::operator==(Date& d)
	{
		if (_year == d._year && _month == d._month && _day == d._day)
		{
			return true;
		}
		return false;
	}
	bool Date::operator!=(Date& d)
	{
		return !(*this == d);
	}
	bool Date::operator<(Date& d)
	{
		if (_year < d._year)
		{
			return true;
		}
		else if(_year==d._year)
		{
			if (_month < d._month)
			{
				return true;
			}
			else if (_month == d._month)
			{
				if (_day < d._day)
				{
					return true;
				}
			}
		}
		return false;
	}
	bool Date::operator>(Date& d)
	{
		return !(*this < d);
	}
	bool Date::operator<=(Date& d)
	{
		return *this < d || *this == d;
	}
	bool Date::operator>=(Date& d)
	{
		return *this > d || *this == d;
	}
	istream& operator>>(istream& in, Date& d)
	{
		in >> d._year >> d._month >> d._day;
		while (!d.CheckDate())
		{
			cout << "日期非法,请重新输入" << endl;
			in >> d._year >> d._month >> d._day;
		}
		return in;
	}
	ostream& operator<<(ostream& out, Date& d)
	{
		out << d._year << "年" << d._month << "月" << d._day << "日";
		return out;
	}
}

相信看到这里你已经收获满满了,我们下期见!

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

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

相关文章

LLMs之Llama 3.1:Llama 3.1的简介、安装和使用方法、案例应用之详细攻略

LLMs之Llama 3.1&#xff1a;Llama 3.1的简介、安装和使用方法、案例应用之详细攻略 导读&#xff1a;2024年7月23日&#xff0c;Meta重磅推出Llama 3.1。本篇文章主要提到了Meta推出的Llama 3.1自然语言生成模型。 背景和痛点 >> 过去开源的大型语言模型在能力和性能上一…

vmware虚拟机安装linux没有IP地址

直接设置固定IP 1、在虚拟机菜单栏选择编辑&#xff0c;然后点击虚拟网络编辑器 2、选择Vmnet8 Net网络连接方式&#xff0c;随意设置子网IP 3、点击NAT设置页面&#xff0c;查看子网掩码和网关&#xff0c;修改静态IP会用到 4、打开电脑控制面板–网络和Internet–网络连…

Visual Studio 智能代码插件:Fitten Code

Fitten Code 是由非十大模型驱动的AI编程助手&#xff0c;它可以自动生成代码&#xff0c;提升开发效率&#xff0c;协助调试 Bug&#xff0c;节省时间。还可以对话聊天&#xff0c;解决编程碰到的问题。 Fitten Code 免费且多种编程语言&#xff0c;包括 Python、C、Javascri…

【CG】计算机图形学(Computer Graphics)基础(其贰)

0 学习视频 B站GAMES101-现代计算机图形学入门-闫令琪 ※ 接上文【CG】计算机图形学&#xff08;Computer Graphics&#xff09;基础&#xff08;其壹&#xff09; 7 光线追踪 7.1 为什么需要光线追踪&#xff1f; 光栅化无法妥善处理全局效果 &#xff08;软&#xff09;阴…

sizeof和strlen区别

如图&#xff0c;sizeof来计算的时候&#xff0c;得出的是计算机用多少个字节来表示一个地址 而strlen来计算的时候&#xff0c;只是计算出他的有效字符长度 打印出的不同地址就是其不同的区别

数据中心同步指南 : 数据中心架构师和其他网络专家需要了解有关 5G 同步的知识

随着 5G 的推出&#xff0c;电信基础设施的设计方式正在发生巨大变化。由于网络运营商希望创建更开放的网络基础设施生态系统&#xff0c;部分基础设施&#xff08;如基带处理&#xff09;被虚拟化并移至电信数据中心。影响数据中心架构的另一个与 5G 相关的趋势是移动边缘计算…

20240727 每日AI必读资讯

&#x1f310;OpenAI向Google宣战&#xff0c;重磅推出AI搜索引擎SearchGPT &#xff01; - 将 AI 与实时网络信息结合 提供生成式UI结果 - SearchGPT 结合网络最新信息可以直接回答问题&#xff0c;同时注明相关来源链接。 - 还可以像与人对话一样提出后续问题&#xff0c;…

YOLOv8 改进 | 注意力机制 | 处理原始SE通道信息丢失问题的ESE【含分割,检测,OBByaml文件】

秋招面试专栏推荐 &#xff1a;深度学习算法工程师面试问题总结【百面算法工程师】——点击即可跳转 &#x1f4a1;&#x1f4a1;&#x1f4a1;本专栏所有程序均经过测试&#xff0c;可成功执行&#x1f4a1;&#x1f4a1;&#x1f4a1; 专栏目录 &#xff1a;《YOLOv8改进有效…

开放式耳机哪个品牌值得入手?五大年度黑马产品推荐

近几年开放式耳机获得了越来越多消费者的青睐&#xff0c;其中十分重要的原因就是&#xff0c;它能在不降低佩戴体验感的同时&#xff0c;让我们在不同的环境中尽情享受音乐。对于刚刚接触开放式耳机的朋友们来说&#xff0c;挑选一款在自己不踩雷的开放式耳机并非易事&#xf…

关于同一型号单片机使用不同版本的Keil5.pack包导致编译找不到对应.h问题

1.单片机型号:STM32G070CBTX 2.过程分析 拿到一个完整的工程&#xff0c;打开后编译报错&#xff0c;一些.h文件找不到&#xff0c;导致一些宏定义报错&#xff0c;如下图所示: 刚开始怀疑是安装的pack问题&#xff0c;又去ARM官网上下载最新的STM32G0xx_DFP.1.5.0版本的pack包…

吴恩达的TranslationAgent学习

TranslationAgent构成 整个[TranslationAgent (github.com)]在流程上分为短文本的一次性翻译和长文本的分chunk翻译&#xff08;按照Token进行划分&#xff09;。 但是不论长文本翻译还是短文本翻译&#xff0c;总体流程遵循执行、纠正再执行的逻辑循环实现。 这种按照自省思路…

Android ContentResolver.loadThumbnail转Kotlin

Android ContentResolver.loadThumbnail转Kotlin loadThumbnail原先是Java实现的&#xff0c;现在抠出来转Kotlin实现。 private fun loadThumbnail(uri: Uri, size: Size, signal: CancellationSignal): Bitmap {return myLoadThumbnail(mContext?.contentResolver!!, uri, s…

基于Qt的视频剪辑

在Qt中进行视频剪辑可以通过多种方式实现&#xff0c;但通常需要使用一些额外的库来处理视频数据。以下是一些常见的方法和步骤&#xff1a; 使用FFmpeg FFmpeg是一个非常强大的多媒体框架&#xff0c;可以用来处理视频和音频数据。你可以使用FFmpeg的命令行工具或者其库来实现…

Qemu和宿主机不使用外网进行文件传输

简介 目前Qemu虚拟机和宿主机交互&#xff0c;最方便的方式是进行网络传输&#xff0c;但也有不能使用外网的情况&#xff0c;这时候使用挂载是比较好的方式。 Linux使用 宿主机&#xff1a;Linux Qemu&#xff1a;Linux 传输方式&#xff1a;挂载 参考&#xff1a;在QEMU虚拟…

深圳市索迪迈科技有限公司:车载视频监控领域的精英

位于科技创新前沿的深圳市&#xff0c;索迪迈科技有限公司是一家专业从事车载视频监控设备研发、生产、销售的高新技术企业。公司自成立起&#xff0c;一直专注于车载设备领域的研发、生产、经营、服务一体化&#xff0c;为市场提供卓越的车载监控产品。 自主专业&#xff0c;专…

MySQL练习(4)

作业要求&#xff1a; 实现过程&#xff1a; 1.新建数据库 2.新建表 3.处理表 &#xff08;1&#xff09;修改student 表中年龄 (sage) 字段属性&#xff0c;数据类型由 int 改变为 smallint 。 &#xff08;2&#xff09;为 Course 表中 Cno 课程号字段设置索引,并查看索引。…

C语言边界互通传送迷宫

目录 注意事项开头程序程序的流程图程序输入与输出的效果结尾 注意事项 程序里有关字符’\033’的输出都关于Sunshine-Linux的其中一篇博客——《printf函数高级用法设置打印字体颜色和背景色等》 开头 大家好&#xff0c;我叫这是我58。今天&#xff0c;我们来看一下我用C语…

dpdk编译安装以及接收udp报文(基于ubuntu)

目录 1、编译 2、设置运行环境 3、使用dpdk接收udp报文 3.1、设置发送端arp信息 3.2、测试 3.3、代码 4、其他 1、编译 代码下载&#xff1a; DPDK 下载版本&#xff1a;DPDK 19.08.2 export RTE_SDK/root/dpdk-stable-19.08.2/ export RTE_TARGETx86_64-native-li…

[杂项] 判断一个C++程序是多少位数属于那种编译版本

C/C程序 使用Process Explore。找到运行的程序后双击。 在image可以看到程序的位数。 view→lower Pane View → DLLs。通过查看vc运行库的dll是否带d后缀可以判断该程序是debug版本还是release版本 dependcyWalker 使用 dependcyWalker 来查看程序的位数及编译版本。注意…