首页 > 编程语言 >c++类开发的第三篇(讲明白友元函数和this指针)

c++类开发的第三篇(讲明白友元函数和this指针)

时间:2024-02-21 17:35:04浏览次数:17  
标签:友元 第三篇 函数 对象 成员 c++ void 指针

friend_function

成员变量和函数的存储

c++实现了封装数据处理数据的操作(函数)是分开存储的。

  1. c++中的非静态数据成员直接内含在类对象中,就像c语言的struct一样。

  2. 成员函数并不会出现在对象中,而是作为类的一部分存储在代码段中,需要通过对象或对象指针进行调用。成员函数可以访问类的所有成员变量和成员函数,包括私有成员,但不能直接访问静态变量,需要使用类名或对象来访问。

  3. 每一个非内联成员函数(non-inline member function)只会诞生一份函数实例.

class Regina02 {
public:
	int a;
};

class Regina03 {
public:
	int mA;
	static int sB;
};

class Regina04 {
public:
	void printMyClass() {
		cout << "Regina04 void printMyClass()" << endl;
	}
public:
	int mA;
	static int sB;

};

class Regina05 {
public:
	void printMyClass() {
		cout << "Regina05 void printMyClass()" << endl;
	}
	static void ShowMyClass() {
		cout << "Regina05 static void ShowMyClass()" << endl;
	}

在这四个类里面,后面一个类分别比前面一个类多一个成分,为了证实C++类对象中的变量和函数是分开存储,我们分别实例化四个对象打印大小

int main() {
	Regina02 regina02;
	Regina03 regina03;
	Regina04 regina04;
	Regina05 regina05;
	cout << "Regina02:" << sizeof(regina02) << endl; //4
	//静态数据成员并不保存在类对象中
	cout << "Regina03:" << sizeof(regina03) << endl; //4
	//非静态成员函数不保存在类对象中
	cout << "Regina04:" << sizeof(regina04) << endl; //4
	//静态成员函数也不保存在类对象中
	cout << "Regina05:" << sizeof(regina05) << endl; //4
	return 0;

image-20240208150345661

发现所有的类对象大小都一样,对于静态成员变量和静态成员函数,它们并不属于类的对象,而是属于整个类本身。它们存储在静态数据区,而不是存储在类的对象中。静态成员变量在程序运行期间只有一份实体,不随类的对象的创建而分配内存,而静态成员函数也不依赖于特定的类对象。

this指针

通过上例我们知道,c++的数据和操作也是分开存储,并且每一个非内联成员函数(non-inline member function)只会诞生一份函数实例,也就是说多个同类型的对象会共用一块代码

那么问题是:这一块代码是如何区分那个对象调用自己的呢?

image-20240208150556604

c++通过提供特殊的对象指针,this指针,解决上述问题。this指针指向被调用的成员函数所属的对象。

c++规定,this指针是隐含在对象成员函数内的一种指针。当一个对象被创建后,它的每一个成员函数都含有一个系统自动生成的隐含指针this,用以保存这个对象的地址,也就是说虽然我们没有写上this指针,编译器在编译的时候也是会加上的。因此this也称为“指向本对象的指针”,this指针并不是对象的一部分,不会影响sizeof(对象)的结果。

 this指针是C++实现封装的一种机制,它将对象和该对象调用的成员函数连接在一起,在外部看来,每一个对象都拥有自己的函数成员。一般情况下,并不写this,而是让系统进行默认设置。

this指针使用

  • 当形参和成员变量同名时,可用this指针来区分

  • 在类的非静态成员函数中返回对象本身,可使用return *this.


class Use_this {
private:
	string name;
	int age;
public:
	/*当形参名字和成员变量名字一样时,this指针可以区分*/
	Use_this(string name, int age) {
		// name = name; false 
		this->name = name;
		this-> age = age;
	}
	//返回对象本身的引用
	//重载赋值操作符
	//其实也是两个参数,其中隐藏了一个this指针
	Use_this personAdd(Use_this& person) {
		string newname = this->name + person.name;
		int newage = person.age + this->age;
		Use_this newperson(newname, newage);
		return Use_this(newname, newage); 

	}
	void showPerson() {
		cout << "name: " << name << "\n" << "age: " << age << endl;
	}

};

int main(){
	Use_this person1 = Use_this("regina ", 13);
	Use_this person2 = Use_this("love Ivanlee", 14);

	person1.showPerson();
	Use_this person3 = person1.personAdd(person2);
	person3.showPerson();
	return 0;
}

image-20240221100829750

const

const 关键字用于修饰成员函数时,它表示该成员函数是一个常量成员函数,也称为常成员函数。常成员函数有以下特点:

  1. 常成员函数不会修改对象的状态:常成员函数承诺不会修改类的任何非静态成员变量。这意味着在常成员函数内部,不能修改成员变量的值,除非该成员变量被声明为 mutable
  2. 常成员函数可以读取对象的状态:常成员函数可以访问和读取类的所有成员变量,包括非 const 成员变量。
  3. 常成员函数可以调用其他常成员函数:常成员函数可以调用其他常成员函数,因为它们都遵守不修改对象状态的承诺。

在 C++ 中,mutable 是一个关键字,用于声明类的成员变量可以在常成员函数中被修改。通常情况下,常成员函数(被 const 修饰的成员函数)不能修改类的成员变量,因为它们承诺不会改变对象的状态。然而,有时候我们可能需要在常成员函数中修改某些变量,这时就可以使用 mutable 关键字。

当一个成员变量被声明为 mutable 时,即使在常成员函数中,该成员变量仍然可以被修改。这样做的目的是为了允许在逻辑上不改变对象状态的情况下,修改一些与对象状态无关的数据。

image-20240221103736592在ida里面写代码的时候就能看到,当我们定义好了const成员函数时,没有定义过的mutable的变量是不可以修改的。

友元函数

友元函数存在的主要原因是为了提供更灵活的设计和实现方式,同时在某些特定情况下确实需要直接访问类的私有成员或保护成员。以下是一些使用友元函数的情况和原因:

  1. 访问私有成员:有时候在类的外部需要访问类的私有成员数据,但又不希望通过公有成员函数来实现。这种情况下,可以使用友元函数来实现对私有成员的直接访问,提高代码的封装性和安全性。
  2. 提高效率:有些操作可能需要直接访问类的私有数据,如果通过公有接口来访问可能会降低效率。通过友元函数可以避免频繁调用公有接口,提高程序执行效率。
  3. 实现非成员函数:某些操作与类密切相关,但又不属于类的成员函数的范畴,这时候可以使用友元函数来实现这样的操作。
  4. 重载运算符:在C++中,一些运算符如 <<>>+ 等是可以重载的,如果想要对类进行运算符重载,并且需要直接访问类的私有数据,可以使用友元函数来实现。(后续会解释)
  5. 类之间的协作:有时候多个类之间需要相互访问对方的私有成员,可以使用友元函数来实现类之间的协作。

友元语法

friend关键字只出现在声明处

其他类、类成员函数、全局函数都可声明为友元

友元函数不是类的成员,不带this指针

友元函数可访问对象任意成员属性,包括私有属性

#include<iostream>
using namespace std;

class Home;// 前向声明 Myfriend 类
class Myfriend {
public:
	void PlayinBedroom(Home& home);
	void Eatindinningroom(Home& home);
};

class Home {
private:
	string bedroom;
	string dinningroom;
public:
	string sittingroom;
	Home() {
		bedroom = "bedroom";
		dinningroom = "dinning room";
	}
	friend void CleanMyhome(Home &home) {
		cout << "友元全局函数访问" << home.bedroom << endl;
	}
	//成员函数做友元函数
	friend void Myfriend::PlayinBedroom(Home& home); //必须把函数内容写在类外面
	friend void Myfriend::Eatindinningroom(Home& home);//类内只做声明
};

void Myfriend::PlayinBedroom(Home& home) {
	std::cout << "我的朋友正在" << home.bedroom << "里玩" << std::endl;
}

void Myfriend::Eatindinningroom(Home& home) {
	std::cout << "我的朋友正在" << home.dinningroom << "里吃饭" << std::endl;
}

int main() {
	Home myHome;
	CleanMyhome(myHome); // 调用全局友元函数
	Myfriend myFriend;
	myFriend.PlayinBedroom(myHome); // 调用 Myfriend 的成员函数作为友元函数
	myFriend.Eatindinningroom(myHome); // 调用 Myfriend 的成员函数作为友元函数

	return 0;
}

image-20240221121441099

[友元类注意]

1.友元关系不能被继承。

2.友元关系是单向的,类A是类B的朋友,但类B不一定是类A的朋友。

友元关系不具有传递性。类B是类A的朋友,类C是类B的朋友,但类C不一定是类A的朋友。

练习题

请编写电视机类,电视机有开机和关机状态,有音量,有频道,提供音量操作的方法,频道操作的方法。由于电视机只能逐一调整频道,不能指定频道,增加遥控类,遥控类除了拥有电视机已有的功能,再增加根据输入调台功能。

首先我们要写一下电视的功能

class TV {
	friend class Remote;
private:
	int mState; //电视状态,开机,还是关机
	int mVolume; //电视机音量
	int mChannel; //电视频道

public:
	enum {On,Off};
	enum {minVol, maxVol = 100};
	enum{ minChannel = 0, maxChannel = 255};
	TV() {
		mState = Off;
		mVolume = minVol;
		mChannel = minChannel;

	}
	void TurnOnorOff() {
		this->mState = (this->mState == Off ? On : Off);
	}

	//调高音量
	void VolumeUp() {
		if (this->mVolume >= maxVol) {
			return;
		}
		this->mVolume++;
	}
	void VolumeDown() {
		if (this->mVolume == minVol) {
			return;
		}
		this->mVolume--;
	}
	//更换电视频道
	void ChannelUp() {
		if (this->mChannel >= maxChannel) {
			return;
		}
		this->mChannel++;
	}
	void ChannelDown() {
		if (this->mChannel <= minChannel) {
			return;
		}
		this->mChannel--;
	}
	//展示当前电视状态信息
	void ShowTeleState() {
		cout << "当前电视" << (mState == On ? "已开机":"已关机") << endl;
		if (mState == On) {
			cout << "当前音量:" << mVolume << endl;
			cout << "当前频道:" << mChannel << endl;
		}
		cout << "-------------" << endl;

	}
class Remote {
private:
	TV tv;
public:
	Remote(TV& tv) {

		this->tv = tv;
	}
	void OnOrOff() {
		tv.TurnOnorOff();
	}
	//调高音量
	void VolumeUp() {
		tv.VolumeUp();
	}
	//调低音量
	void VolumeDown() {
		tv.VolumeDown();
	}
	//更换电视频道
	void ChannelUp() {
		tv.ChannelUp();
	}
	void ChannelDown() {
		tv.ChannelDown();
	}
	//设置频道 遥控新增功能
	void setChannel(int chan) {
		if (chan < tv.minChannel || chan > tv.maxChannel) {
			return;
		}
		tv.mChannel = chan;
	}

	//展示电视信息
	void showTV() {
		tv.ShowTeleState();
	}
};
//直接操作电视
void test01() {

	TV television;
	television.ShowTeleState();
	television.TurnOnorOff(); //开机
	television.VolumeUp(); //增加音量+1
	television.VolumeUp(); //增加音量+1
	television.VolumeUp(); //增加音量+1
	television.VolumeUp(); //增加音量+1
	television.ChannelUp(); //频道+1
	television.ChannelUp(); //频道+1
	television.ShowTeleState();
}//通过遥控操作电视

void test02() {
	//创建电视
	TV television;
	//创建遥控
	Remote remote(television);
	remote.OnOrOff();
	remote.ChannelUp();//频道+1
	remote.ChannelUp();//频道+1
	remote.ChannelUp();//频道+1
	remote.VolumeUp();//音量+1
	remote.VolumeUp();//音量+1
	remote.VolumeUp();//音量+1
	remote.VolumeUp();//音量+1
	remote.showTV();
}

image-20240221172730344

标签:友元,第三篇,函数,对象,成员,c++,void,指针
From: https://www.cnblogs.com/ivanlee717/p/18025808

相关文章

  • C++开发基础知识(修改)
    2024-01-0820:13星期一博客内容来自相关书籍和网站内容总结,仅供个人参考使用:笔者@StuBoo使用目录快速转到技术面试问题汇总、算法笔记1.C++语言基础1.1语言特性面向对象编程(OOP):C++支持面向对象编程,包括封装、继承和多态。通过类和对象,可以将数据和方法组织成单个单元,......
  • c++ 2 字母异位词
    //字母异位词是由重新排列源单词的所有字母得到的一个新单词。//示例1:////输入:strs=["eat","tea","tan","ate","nat","bat"]//输出:[["bat"],["nat","tan"],["ate","eat",&q......
  • 1 c++算法题解析-两个数之和
    //给定一个整数数组nums和一个整数目标值target,请你在该数组中找出和为目标值target的那两个整数,并返回它们的数组下标。//你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。//你可以按任意顺序返回答案。//示例1:////输入:nums=[2,7,......
  • C++限制函数最大执行时间
    背景C++调用某些硬件操作(如TPU推理)可能存在超时风险,需要限制函数的执行时间。思考异步执行免不了开线程,如何限制join的最大时间是关键。设计如下函数:boolInfer(uinttimeout_ms)根据输入的timeout_ms参数,按时完成返回true超时返回false。实现使用std::mutex配合std::con......
  • Bubbliiiing版本yolov7 c++opencv dnn部署
    使用B导的yolov7代码部署,代码地址:https://github.com/bubbliiiing/yolov7-pytorch 模型的的训练看B导即可,up主地址:Bubbliiiing的博客_CSDN博客-神经网络学习小记录,睿智的目标检测,有趣的数据结构算法领域博主 模型训练完成之后,在predict.py中设置mode="export_onnx"即可......
  • 基于OpenVINO 2022.1 C++ API部署YOLOv7预训练模型
    任务背景作为视觉应用中最常见的任务之一,目标检测一直是各类新模型刷榜的必争之地,其中就以YOLO系列的网络结构最为突出。YOLO的全称是youonlylookonce,指只通过one-stage的方式需要“浏览一次”就可以识别出图中的物体的类别和位置。近期YOLO官方团队又放出新版本——YOLOv7,速......
  • c++ 直接读取 cpu id
    c++直接读取cpuid#include<iostream>usingnamespacestd;#include<string>#include<comutil.h>#include"Windows.h"#include<atlconv.h>#include<intrin.h>#include<cctype>#include<iomanip>char*......
  • C++多线程 第八章 设计并发代码
    第八章设计并发代码数据划分工作在处理开始前在线程间划分数据方面,C++与MPI或OpenMP的方式较为相似.一个任务被分成一个并行任务集,工作的线程独立运行这些任务.并且在最后的化简步骤中合并这些结果.尽管这种方法是很有效的,但是只有在数据可以实现划分时,才可如此.考虑这......
  • 【C++】判断回文字符串。回文指的是顺读和逆读都一样的字符串。例如,“tot”和“otto”
    //判断字符串是否是回文字符串(考虑大小写,空格和标点符号)boolpalindrome1(string&str){stringret;for(auto&c:str){if(isalpha(c)){if(isupper(c)){ret.push_back(tolower(c));}else{ret.push_back(c);}......
  • C++ 模板的笔记2
    C++模板的笔记2关于可变参函数模板借鉴了一部分笔记,感谢大佬类模板中的嵌套类模板可以嵌套其他类模板,就像普通类可以嵌套其他普通类一样。嵌套的类模板可以访问外部类模板的成员,包括私有成员。示例:#include<iostream>usingnamespacestd;template<typenameT>classO......