类和对象——(初始化列表,explicit关键字,static成员,友元)

文章目录

    • 1.初始化列表
    • 2.explicit关键字
    • 3.static成员
      • 概念:
    • 4.友元
      • 4.1友元函数
      • 4.2友元类

1.初始化列表

初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式。
示例:

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

private:
	int _year;//声明
	int _month;
	int _day;
};

注意:

  1. 每个成员变量在初始化列表中只能出现一次(即初始化一次)
  2. 类中包含一下成员的,必须放在初始化列表初始化
  • 引用成员变量
  • const成员变量
  • 自定义类型成员(没默认构造函数)
class B
{
     
public:
	B(int b)
		: _ret(b)
	{
     }

private:
	int _ret;
};

class A
{
     
public:
	A(int a, int n)
		: _a(a)
		, _n(n)
		, _b(5)
	{
     }

private:
	int& _a;//引用
	const int _n;//const
	B _b;//自定义类型
};
  1. 对于内置类型使用初始化列表或者函数体内初始化都行,对于自定义类型建议使用初始化列表。
  2. 成员变量在类中的声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关。(尽量保证声明与初始化列表顺序一致)

2.explicit关键字

构造函数不仅可以构造与初始化对象,对于单个参数的构造函数,还具有类型转换的作用。
匿名对象:

class A
{
     
public:
	explicit A(int n)
		: _n(n)
	{
     }
	A(const A& a)
		: _n(a._n)
	{
     }	
private:
	int _n;
};

int main()
{
     
	A a1(1);//他的生命周期在main函数域
	A(5);//匿名对象,他的生命周期在这一行
	
	A a2 = 5;
	//隐式类型转换,结果调用构造函数 -> 先构造一个A(5)匿名临时对象,
	//再用临时对象拷贝构造a2
	return 0;
}

用explicit修饰构造函数,将会禁止单参构造函数的隐式转换。

3.static成员

概念:

声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的成员函数,称之为静态成员函数。

面试题实现一个类,计算中程序中创建出了多少个类对象?

class A
{
     
public:
	A()
	{
     
		++count_A;
	}

	A(const A& a)
	{
     
		++count_A;
	}

	//静态成员函数没有this指针
	static int GetCountA()
	{
     
		return count_A;
	}

private:
	//静态成员变量属于类的所有对象,属于整个类
	static int count_A;
};

//定义初始化
int A::count_A = 0;

int main()
{
     
	A a1;
	A a2;
	A a3;
	A a4(a1);
	A a5(a2);
	
	//如果没有访问限定符的限制,指定类域就可以访问静态成员
	cout << A::count_A << endl;
	
	cout << a1.GetCountA() << endl;//对象.访问
	cout << A::GetCountA() << endl;//类名::访问
	return 0;
}

注意:

  1. 静态成员变量属于类的所有对象,属于整个类。
  2. 静态成员变量必须在类外定义,定义时不添加static关键字。
  3. 类静态成员即可用 : 1:类名::静态成员 2:对象.静态成员来访问。
  4. 静态成员函数没有隐藏的this指针,不能访问任何非静态成员。

4.友元

友元提供了一种突破封装的方式,有时候提供了便利。但是友元一定程度上破坏封装性,所以不宜多用。

4.1友元函数

现在我们尝试一段代码去重载operator<<,然后发现我们没办法将operator<<重载成成员函数。原因是cout的输出流对象和隐含的this指针在抢占第一个参数的位置

class Date
{
     
public:
	Date(int year, int month, int day)
		: _year(year)
		, _month(month)
		, _day(day)
	{
     }
	ostream& operator<<(ostream& out)
	{
     
		out << d._year << "-" << d._month << "-" << d._day;
		return out;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
     
	Date d(2021, 3, 9);
	cout << d;
	return 0;
}

这里要用到友元函数来解决这个问题!!!

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

class Date
{
     
	friend ostream& operator<<(ostream& out, const Date& d);
public:
	Date(int year, int month, int day)
		: _year(year)
		, _month(month)
		, _day(day)
	{
     }
private:
	int _year;
	int _month;
	int _day;
};
ostream& operator<<(ostream& out, const Date& d)
{
     
	out << d._year << "-" << d._month << "-" << d._day;
	return out;
}

int main()
{
     
	Date d(2021, 3, 9);
	cout << d << endl;
	return 0;
}

在这里插入图片描述
友元函数的特点:

友元函数可访问类的私有和保护成员,但不是类的成员函数。
友元函数不能用const修饰。
友元函数可以在类定义的任何地方声明,不受类访问限定符限制。
一个函数可以是多个类的友元函数。
友元函数的调用与普通函数的调用和原理相同。

4.2友元类

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

  1. 友元关系是单向的,不具有交换性。
  2. 友元关系不能传递。
class Date; // 前置声明
class Time
{
     
	friend class Date; // 声明日期类为时间类的友元类,则在日期类中就直接访问Time类中的私有成
	员变量
public:
	Time(int hour, int minute, int second)
		: _hour(hour)
		, _minute(minute)
		, _second(second)
	{
     }
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
     
public:
	Date(int year = 1900, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{
     }
	void SetTimeOfDate(int hour, int minute, int second)
	{
     
		// 直接访问时间类私有的成员变量
		_t._hour = hour;
		_t._minute = minute;
		_t.second = second;
	}
private:
	int _year;
	int _month;
	int _day;
	Time _t;
};

上述代码中声明了Date类Time类的友元类,则Date类可以直接访问Time类的私有成员变量,但是Time类不能访问Date类。

你可能感兴趣的:(c++,c++,类)