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

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

posted @ 2024-02-21 17:30  ivanlee717  阅读(65)  评论(0编辑  收藏  举报