类和对象收尾

本文主要内容:

  • 初始化列表
  • 静态成员,成员函数
  • 友元

1.初始化列表
今天我们来进行类和对象的收尾,相比于前面的构造函数和析构函数来说,这篇文章的内容相对是比较好理解的。我们这篇博客就来提一提静态成员,成员函数还有前面稍微提到的初始化列表。
所谓的初始化列表就是成员变量被定义的地方!你可能会有疑问,在构造一个对象的时候,整个对象都出来了,为什么还要特意给成员变量找一个定义的地方。有这个疑问是很正常的。来看看下面这段代码:

class Demo
{
private:
	int _a;
	const int _b;//const常量必须在定义的时候初始化
	int& _rc;//引用也必须在定义的时候初始化
};

正是因为有的类的成员变量必须在定义的时候就要初始化,所以才必须找一个定义的地方。而成员定义的地方正好就是初始化列表,所以这个Demo类的构造函数可以这么写:

class Demo
{public:
	Demo(int a=4)
		: _a(4)
		, _b(3)
		, _rc(a)
	{}
private:
	int _a;
	const int _b;
	int& _rc;
};

也就是说,每一次调用构造函数,所有的成员变量都会走一次初始化列表。不仅如此,初始化列表还可以应用在下面这种情况:

//初始化列表解决无法生成默认构造函数的问题
class A
{  public:
	A(int x)
	{
		_x = x;
	}
private:
	int _x;
};
class Demo
{public:
	Demo(int a=4)
		: _a(4)
		, _b(3)
		, _rc(a)
		,_aa(5)
	{}
private:
	int _a;
	const int _b;
	int& _rc;
	A _aa;
};

本来正常情况下,我们是没有办法提供Demo类的默认构造函数,因为类A没有默认构造函数,但是利用初始化列表我们就可以很好解决这个问题。
另外,初始化列表的初始化顺序只和类成员的声明顺序有关,而和初始化顺序无关

class B
{
public:
	B()
		:_b1(4)
		,_b2(_b1)
	{}
private:
	int _b2;
	int _b1;
};
int main()
{   B b;
   return 0;
}

打开监视窗口,观察到如下的信息:
在这里插入图片描述
如果初始化列表里面的顺序来看,最后_b1,_b2的值应该都被初始化成4,但是很明显这里的_b2却是随机值。因为初始化列表的初始化是按照成员声明的顺序来的!所以说这里先用_b1初始化_b2,因此_b2是随机值!在使用初始化列表的时候要特别注意这一点。


2.静态成员,静态成员函数
前面我们类所拥有的成员都是依赖于实例化对象的。那么在C++里面还提供了一种特殊的机制---->静态成员,静态成员函数,那么这两个东西是不需要依赖对象---->可以理解成,静态成员和静态成员函数为所有类对象所共有的。
我们可以来看一看具体的静态成员怎么定义:

class C
{ 
private:
	int _a;
	static char _c;
};

那么静态成员的初始化比较特殊,由于静态成员的特殊性,因此静态成员并不会在初始化列表定义。我们必须显式自己初始化静态成员。

//这里为了演示,把静态成员放出来
class C
{   privateint _a;
	public:
	static char _c;
};
//静态成员必须类外初始化,因为静态成员不会走初始化列表
C::_c=1;

讲完了静态成员,我们在来看看静态成员函数,语法声明也是在成员函数前面加一个static关键字。

class Demo
{
  public:
  //这就是一个静态的成员函数
    static void show()
    {
      cout<<"hello world!"<<endl;
    }
};

访问静态成员函数的方法有两种:

1.使用实例化对象访问
2.使用类名::函数名进行访问

思考两个问题:

1.普通成员函数能否调用静态成员函数?
2.静态成员函数能否调用普通成员函数?

答案是:普通成员函数可以调用静态成员函数,而静态成员函数不能普通成员函数。因为,静态成员函数没有this指针!


3.友元
以日期类为例,有的时候我们希望通过键盘输入来构造日期类对象,那么我们就需要对日期类进行流提取运算符的重载,假如我们把流提取运算符重载成成员函数

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	Date(const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
	}
	Date& operator=(const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;
	}
	//标准库流对象
	istream& operator>>(istream& in)
	{
	    in>>_year>>_month>>_day;
	    return in;
	}

private:
	int _year;
	int _month;
	int _day;
};
//具体调用
int main()
{
   Date d1;
   //cin>>d1是错误的
   d1>>cin;//这个是正确调用!
  return 0;
}

出现这种情况的原因就是:两个操作数的函数,编译器会自动把左操作数作为第一个参数。而成员函数的第一个参数永远是this指针,我们没办法更改!所以为了我们能够写法更加自然和易于理解,最好能重载全局的流提取运算符。但是又有一个问题。私有的成员变量外部无法直接访问。因此C++提供了一种特殊的机制来解决---->友元
我们先来看一看怎么使用友元来使得访问私有成员:

class Date
{
public:
  //友元声明---->friend关键字
  friend istream& operator>>(Date& d);
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	Date(const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
	}
	Date& operator=(const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;
	}
	

private:
	int _year;
	int _month;
	int _day;
};
//重载全局的流提取
istream& operator(Date& d)
{
   in>>d._year>>d._month>>d._day;
   return in;
}

经过上面的操作以后,我们就可以使用cin来提取日期类的信息了!
友元有以下几个注意的地方:

  • 友元关系是单向的,A是B的友元,但是B的友元不是A!
  • 友元没有传递性!
  • 友元会破坏封装,尽量避免使用友元

总结

  • 初始化列表是成员定义的地方
  • 静态成员,函数是属于类的,静态成员函数没有this指针
  • 友元的简单使用

本文的主要内容就到这里,如有不足支持还望指出,希望大家一起共同进步!

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值