C++类和对象-5

本篇博客来讲述C++类和对象中的最后一些内容,即友元和const的使用方法。

目录

1.友元

1.1引入

1.2内容

1.2.1友元函数

1.2.2友元类

1.3内部类

2.const修饰

2.1内容

2.1.1常数据成员

2.1.2常成员函数

2.1.3常对象

2.2示例

1.友元

1.1引入

在讲述友元之前,我们来简单了解一下友元诞生的原因。当我们封装类后,对于其中私有成员的访问我们需要提供具体接口来返回,这样我们才可以在外部对类中的私有成员进行访问,如下所示代码:

#include

#define Pi 3.14

class Circle {
private:
	//圆心坐标
	int x;
	int y;
	//半径
	int r;
public:
	Circle(int x = 0, int y = 0, int r = 0) {
		this->x = x;
		this->y = y;
		this->r = r;
	}
	int Get_r() {//提供对外接口用于访问类中对应私有成员
		return r;
	}
};

double SquareMeasure(Circle& c1) {
	return c1.Get_r() * c1.Get_r() * Pi;
}

int main() {
	Circle c1(0, 0, 1);
	std::cout << SquareMeasure(c1);
	return 0;

其中Get_r接口便是提供的对外访问类中私有成员的接口,不过在具体的SquareMeasure函数中我们对于Get_r接口调用次数较为重复,降低了程序的执行效率,所以在此基础上,提出友元的概念。

1.2内容

1.2.1友元函数

友元提供了一种突破封装的方式,有时会为我们提供一定程度的便利。但是友元在另一种程度上会增加代码的耦合性,破坏了原有的封装,对于其具体的使用方式我们在上述代码的基础上改变。

#include

#define Pi 3.14

class Circle {
private:
	//圆心坐标
	int x;
	int y;
	//半径
	int r;
public:
	Circle(int x = 0, int y = 0, int r = 0) {
		this->x = x;
		this->y = y;
		this->r = r;
	}
	friend double SquareMeasure(Circle& c1);
};

double SquareMeasure(Circle& c1) {
	return c1.r * c1.r * Pi;
}

int main() {
	Circle c1(0, 0, 1);
	std::cout << SquareMeasure(c1);
	return 0;
}

我们在Circle类中声明SquareMeasure函数为友元,则在接下来的SquareMeasure函数中,我们可以直接访问Circle类中的私有数据成员,并且在使用过程中,它存在几种特征:

  • 友元函数不是当前类中的成员函数,而是独立于当前类的外部函数,但他可以访问类中的所有成员;
  • 友元函数可以在类定义的任何地方声明,不受访问限定符限制;
  • 友元函数可以定义在类的内部,也可以定义在类的内部。
  • 友元函数不能使用const修饰;
  • 一个函数可以是多个类的友元函数;
  • 友元函数的调用和普通函数的调用原理相同。

1.2.2友元类

friend关键字除修饰函数之外,还可以用于修饰类,被修饰的类则称之为友元类。友元类中的所有成员函数都可以是另一个类的友元函数,都可以访问一个类中的非共有成员。

 同样的,我们借助具体的代码来进行展示和理解:

#include

#define Pi 3.14

class Circle {
private:
	//圆心坐标
	int x;
	int y;
	//半径
	int r;
	friend class Size;
public:
	Circle(int x = 0, int y = 0, int r = 0) {
		this->x = x;
		this->y = y;
		this->r = r;
	}
};

class Size {
private:
	double c;//周长
	double s;//面积
public:
	Size(Circle& c1) {
		c = 2 * Pi * c1.r;
		s = c1.r * c1.r * Pi;
	}
	void CircleShow() {
		std::cout << "周长:" << c << std::endl;
		std::cout << "面积:" << s << std::endl;
	}
};


int main() {
	Circle c1(0, 0, 1);
	Size c2(c1);
	c2.CircleShow();
	return 0;
}

很直观,Size类是Circle类的友元,我们可以在Size类中直接访问Circle类中的私有成员,在使用过程中,它存在以下几种特征:

  • 友元关系是单向的,不具有交换性,即在上述案例中我们可以在Size类中直接访问Circle类中的私有成员,但是无法在Circle类中访问Size类的私有成员;
  • 友元关系不具备传递性,即如果B是A的友元,C是B的友元,但是C并不是A的友元;
  • 友元关系并不能继承,即B是A的友元,C继承于A,但是B并不是C的友元。

1.3内部类

当存在一个类直接定义在另一个类的范围,则该类被称之为内部类。内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员,外部类并不具备对内部类的优越访问权限。

不过,内部类是外部类的友元,内部类可以通过外部类的对象参数来访问外部类中的所有成员,但外部类并不是内部类的友元,我们来看代码:

#include

class A {
private:
	int k;
public:
	A(int k = 0) {
		this->k = k;
	}
	class B {//内部类B天生便是外部类A的友元
	public:
		void Show(const A& a1) {
			std::cout << a1.k;
		}
	};
};

int main() {
	A::B a2;
	a2.Show(A());
	return 0;
}

注意在定义B类对象时,加上具体的作用域限定符。内部类的特征我们可以总结为以下几点:

  • 内部类有定义在外部类的任何地方,不受访问限定符限制;
  • 内部类可以直接访问外部类中的static成员,不需要外部类的对象或是类名;
  • sizeof(外部类),只和外部类有关,和内部类无关。

2.const修饰

2.1内容

对于一些数据我们在进行共享的同时,并不希望它们发生改变,于是我们可以加入const修饰来对这些数据进行保护,使其变为常量,常量在程序运行过程中是不允许改变的。

我们根据const修饰的内容不同,可以将其分为三类,分别是:

  • 常数据成员--const修饰数据成员;
  • 常成员函数--const修饰成员函数;
  • 常对象--const修饰类的对象。

2.1.1常数据成员

在类中定义了常数据成员,则构造函数只能通过初始化列表对其进行初始化,并且其他函数都不能对常数据成员进行修改,只能进行访问。

2.1.2常成员函数

常成员函数的原型进行声明时,我们需要在其函数定义的首部使用关键字从const,并且常成员函数不能修改本类的数据成员,也不能调用普通的成员函数,这样可以保证在常成员函数当中不会修改数据成员的值。(关键字const可以当作函数重载的标志)

2.1.3常对象

常对象必须在定义时进行初始化,并且初始化之后的数据不能被更新,不能被改变,因此通过常对象只能调用常成员函数,而不能调用类中的其他普通成员函数。

2.2示例

#include

using namespace std;

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; // 日
};

void Test() {
	Date d1(2022, 1, 13);

	d1.Print();

	const Date d2(2022, 1, 13);

	d2.Print();
}

int main() {
	Test();
	return 0;
}

对于上面代码的执行结果为:

C++类和对象-5_第1张图片

我们可以发现结果的执行结果如函数重载时的模样。

这是因为当我们在使用const修饰成员函数之后,编译器会自动将其优化成如下情况:

	void Print(const Date* this) {

	}

 即默认的this指针加入const修饰,进而形成重载。

你可能感兴趣的:(c++,开发语言)