波奇学C++:友元函数,友元类,内部类,匿名对象,优化构造

友元函数/类 :突破访问限定符,允许非同一个类的函数或者类访私有成员变量。

class A
{
public:
	A()
		:_a(0)
		,_b(1)
	{
		cout << "A()" << endl;
	}
private:
	int _a = 0;
	int _b = 1;
};
void fun(const A& a)
{
	cout << a._a << endl;
}

这个代码会报错,原因是_a是a._a的私有成员,fun()是类外函数不能访问。

加上友元就可以突破限制

class A
{
	friend void fun(const A& a);
public:
	A()
		:_a(0)
		,_b(1)
	{
		cout << "A()" << endl;
	}
private:
	int _a = 0;
	int _b = 1;
};
void fun(const A& a)
{
	cout << a._a << endl;
}

关于友元的知识

友元函数是一种声明,表明这个函数可以访问私有变量。

友元函数本质上不属于类函数,所以这个声明是不受访问限定符限制的,可以定义在public或者private中。

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

友元函数会破快封装性,所以尽量不要用友元。

友元类:

class A
{
	friend class B;
public:
	A()
		:_a(0)
		,_b(1)
	{
		cout << "A()" << endl;
	}
private:
	int _a = 0;
	int _b = 1;
};
class B
{
public:
	void fun(const A& a)
	{
		cout << a._a << endl;
	}
private:
	int _c = 1;
	
};

将fun封装到class B中,在A中用友元声明,使得fun()函数可以访问A中的私有成员变量。

内部类是在类中再嵌套一个类。

class A
{
	
public:
	class B
	{
	public:
		void fun(const A& a)
		{
			cout << a._a << endl;
		}
	private:
		int _c = 1;

	};
	A()
		:_a(0)
		,_b(1)
	{
		cout << "A()" << endl;
	}
private:
	int _a = 0;
	int _b = 1;
};

如图在class A中定义一个类class B

计算类的大小

cout << sizeof(A) << endl;//结果为8

我们知道计算类的大小是不计算函数的,那么B类为什么不会计算到其中,因为B类在其中相当于声明。当然class A也是一种声明,本身应该不应该有大小,sizeof是计算创建A的实例的大小。sizeof(A)可以看成创建类A的实例后计算大小,如果创建A的实例是不会有类B成员的,只有_a,_b成员。把代码修改成

class A
{
	
public:
	class B
	{
	public:
		void fun(const A& a)
		{
			cout << a._a << endl;
		}
	private:
		int _c = 1;

	};
	A()
		:_a(0)
		,_b(1)
	{
		cout << "A()" << endl;
	}
private:
	int _a = 0;
	int _b = 1;
	B b;
};

结果是12,计算了B类成员的大小。

1.类声明本身是没有大小的,相当于图纸。

2.sizeof(类),计算的是类的实例的大小。如果类创建的成员变量有内部类实例,那么会计算内部类的大小,如果成员变量没有,只有类声明那就不算内部类大小。

3.类大小只算成员变量大小。

内部类的性质

内部类是类的友元类,可以在内部类访问类的私有变量,但是类不是内部类的友元类,所以不能访问内部类的私有成员变量。

B可以访问_a,_b,A不能访问_c

匿名对象

class A
{
public:
	A()
		:_a(0)
		, _b(1)
	{
		cout << "A()" << endl;
	}
	void fun()
	{
		cout << "fun()" << endl;
	}
private:
	int _a = 0;
	int _b = 1;
};
A a;//有名对象
A();//匿名对象

有名对象无匿名对象的区别:

有名对象--生命周期在当前函数局部域。

匿名对象--生命周期在当前行。/这行代码运行结束后就会调用析构函数。*‘

有名对象不能加括号:不知道是对象还是函数名

A a();/无法分辨是对象还是函数,传参初始化是可以加。

匿名对象和临时对象具有常性。

const A&aa=A();

匿名对象如果被引用不会变成野引用 const引用延长了匿名对象的生命周期,生命周期在当前函数局部域,就是函数结束时才调用析构函数。

匿名函数的使用场景:更简单地调用类成员函数

//第一种方式
A a;
a.fun();
//第二种方式
A().fun();

传参时省略创建实例

编译器会对同一条表达式调用多个构造函数时进行优化

如拷贝构造+构造函数可能直接被优化为构造函数。

A a=2;

这发生类型转换,先生成临时变量const A b(2),临时变量再对A拷贝构造A a(const A b),编译会直接优化成A a(2)不调用临时拷贝,但用const A& c=2,可以证明临时变量确实会生成。

接收传值时优化

返回A时有拷贝构造,生成临时对象,生成的临时对象初始化ra也调用拷贝构造。(不是等号运算符重载,因为A ra是被初始化的)

 编译器会优化成两次拷贝构造会被合成一次拷贝构造。

//避免的写法
A ra;
A ra=fun5();
//这就不是两次构造了

 传参时优化

void fun6(A a)
{
}
//不是同一行表达式,不会优化,构造+拷贝构造
A a(1);
fun6(a);
//匿名,会优化
fun6(A(1));
//最佳写法,隐式类型转换,会优化
fun6(1);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值