C++类和对象(下)(2)

一、友元

友元提供了一种突破封装的方式,有时提供了方便,但是友元会增加耦合度,破坏了封装,所以不建议经常使用友元。

友元分为: 友元函数友元类

1.1 友元函数

 我们在尝试去重载operator<<时发现无法将operator<<重载成成员函数,因为cout的输出流对象和隐含的this指针在抢占第一个参数的位置。this指针默认是第一个参数也就是左操作数了,但是实际使用中cout需要时第一个形参对象,才能正常使用,所以将operator<<重载成了全局函数,但是又会导致类外没办法访问成员,此时就需要友元来解决,operator>>也是同理。

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

	ostream& operator<<(ostream& _cout)
	{
		_cout << _year << " " << _month << " " << _day << endl;

		return _cout;
	}
private:
	int _year;
	int _month;
	int _day;
};


int main()
{
	Date d1(2022, 1, 1);
	d1 << cout;//形式不符合常规调用

	return 0;
}

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

下面我们来看下友元函数的应用: 

class Date
{
	friend ostream& operator<<(ostream& _cout, const Date& d);
	friend istream& operator>>(istream& _cin, Date& d);
public:
	Date(int year = 2004, int month = 1, int day = 1)
		:_year(year)
		,_month(month)
		,_day(day)
	{}

private:
	int _year;
	int _month;
	int _day;
};

ostream& operator<<(ostream& _cout, const Date& d)
{
	_cout << d._year << " " << d._month << " " << d._day << endl;

	return _cout;
}

istream& operator>>(istream& _cin, Date& d)
{
	_cin >> d._year >> d._month >> d._day;
	return _cin;
}

int main()
{
	Date d1(2022, 1, 1);
	//d1 << cout;//形式不符合常规调用
	Date d;
	cin >> d;
	cout << d << endl;

	return 0;
}

注意:

1.友元函数可访问类的私有成员和保护成员 ,但它并不是类的成员函数

2.友元函数不能用const修饰

3.友元函数可以在类定义的任何地方声明,不受类的访问限定符限制

4.一个函数可以是多个类的友元函数

5.友元函数的调用原理和普通函数的调用原理相同

 2.2 友元类

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

注意:

1.友元的关系是单向的,不具有双向性

例如:下面的代码实例中Time类和Date类,在Time类中声明date类为其友元类,那么可以在Date类中直接访问Time类的私有成员函数,但想在Time类中访问Date类中私有的成员变量则不行。

2.友元关系不可传递

3.友元关系不能继承(关于继承方面以后再来进行介绍)

#include<iostream>
using namespace std;
class Time
{
	//将Date声明为Time的友元类
	friend class Date;
public:
	Time(int hour = 0, int minute = 0, int second = 0)
		:_hour(hour)
		,_minute(minute)
		,_second(second)
	{}

private:
	int _hour;
	int _minute;
	int _second;
};

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

	void SetTimeofDate(int hour, int minute, int second)
	{
		//声明成友元后,Date可以直接访问Time类内的私有和保护成员
		_t._hour = hour;
		_t._minute = minute;
		_t._second = second;
	}


private:
	int _year;
	int _month;
	int _day;
	Time _t;
};

二、内部类

概念:如果一个类定义在另一个类的内部,那么这个定义在类的内部的类就叫做内部类。

内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象来访问内部类的成员。外部类对于内部类没有任何优越的访问权限。但是内部类是其外部类的友元类内部类可以通过外部类的对象参数来访问外部类的所有成员,但是外部类不是内部类的友元。

也就是说内部类和外部类,它们两个类时独立的,平行的,内部类是个独立的类,放在外部类里面,仅仅受到类域的印象。而且内部类可以放在外部类的私有区,那样的话内部类就是外部类的专属类。

特性:

1.内部类可以定义在外部类的public,protected,private都是可以的

2.注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名

3.sizeof(外部类) = 外部类,和内部类没有任何关系

class A
{
private:
	static int k;
	int h;
public:
	//B就是A的内部类,并且B是A的友元
	class B
	{
	public:
		void foo(const A& a)
		{
			cout << k << endl;
			cout << a.h << endl;
		}

	};
};

int A::k = 1;

int main()
{
	A::B b;
	b.foo(A());


	return 0;
}

三、匿名对象

关于匿名对象我们先看下面的一段代码来感受一下:

class N
{
public:
	//构造函数
	N(int a = 0)
		:_a(a)
	{
		cout << "N(int a)" << endl;
	}
	//析构函数
	~N()
	{
		cout << "~N()" << endl;
	}

private:
	int _a;
};

class Solution
{
public:
	int Sum_solution(int n)
	{
		return n;
	}
};


int main()
{
	/*A::B b;
	b.foo(A());*/

	N aa1;
	
	//不能像下面这样去定义对象,这样的话编译器会无法识别到底是函数声明还是对象定义
	//A aa1();

	//我们可以定义匿名对象,匿名对象特点就是不需要名字
	//但是它的声明周期就只要一行,过了定义的这一行就会直接调用析构函数销毁

	N();//匿名对象


	N aa2(2);

	//匿名对象在如下场景很好用,当然还有一些其他的使用场景
	Solution().Sum_solution(10);

	return 0;
}

注意:

1.匿名对象和拷贝时的临时变量都是具有常性的

2.匿名对象生命周期仅在这一行,执行之后就会直接调用析构函数销毁

 四、拷贝对象时的一些编译器优化

在传参和传返回值的过程中,一般编译器会做一些优化,减少对象的拷贝,也是减少了一些时间上的消耗。

在传值传参的时候,先要进行拷贝构造。

对于优化的具体实现这里不做解释,我们只需要记住一般编译器的优化规则:

f(1);

隐式类型,连续构造+拷贝构造->优化为直接构造

f1(A(2));

一个表达式中,连续构造+拷贝构造->优化为一个构造

A aa2 = f2();

一个表达式,连续拷贝构造+拷贝构造->优化为一个拷贝构造

aa1 = f2()

一个表达式中,连续拷贝构造+赋值重载->无法优化

这里的等号是赋值重载,拷贝构造和赋值重载在一起时不能优化的,建议尽量不要用以上写法。

 对于优化这部分到这里就介绍完了。

下面我再来对构造和析构的细节进行一些补充:

五、关于构造和析构的一些补充

1.构造和析构是在栈帧里面的满足先进后出,后进先出的规则,因此后定义的对象先析构,先定义的对象后析构(构造是先定义的先构造,后定义的后构造,构造就相当于入栈,析构是出栈)。

2.若为全局变量,main函数之前就构造了。

3.局部的静态变量在第一次调用的时候构造,第二次调用不会构造,只会初始化一次。

(静态变量只能初始化一次)

4.先构造全局,再构造静态,然后再试普通对象

结语:

关于类和对象的内容,至此就结束了,希望大家能从这几篇博客中对类和对象有更加深刻的认识,接下来会继续更新C++的内容。

  • 14
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

凪よ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值