C++初阶学习第五弹——类与对象(下)——类与对象的收官战

2024-06-14 21:17:56 浏览数 (1)

前言:

在前面我们已经学习了C 类与对象的关键操作——默认成员函数,今天我们对之前的内容进行一些补充,同时再来学习几个新的知识点Static成员、友元和内部类


一、构造函数进阶

1.1 构造函数的赋值化

在前面,我们说对一个自定义类型的变量,当我们定义时可以通过构造函数默认初始化,操作如下:

代码语言:javascript复制
class Date
{
public:
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1();
	return 0;
}

实际上这样的操作并不叫作初始化,更确切的说应该叫赋值化,因为初始化只能有一次,而这个操作却可以多次赋值

比如:

代码语言:javascript复制
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
		_year = 3;
	}

这里就对_year进行了两次赋值,所以这个操作不能称之为初始化,而应该称之为赋值

1.2 构造函数的初始化

构造函数的初始化应该是这样一种形式:

代码语言:javascript复制
class Date
{
public:
	Date(int year, int month, int day)
		:_year(year),
		_month(month),
		_day(day)
	{

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

有这样几个注意事项:

1、类中成员初始化时一定要按照它们声明的顺序来进行,尽量不要跳跃 2、每个成员只能在初始化列表中出现一次(初始化只能是一次) 3、类中包含以下成员,必须放在初始化列表位置进行初始化: · 引用成员变量 · const成员变量 · 自定义类型成员(且该类没有默认构造函数时)

上面提到的一定要注意对于引用成员变量、const成员变量、自定义类型成员一定要在初始化列表中进行初始化:

代码语言:javascript复制
class A
{
public:
	A(int a)
		:_a(a)
	{}
private:
	int _a;
};
class B
{
public:
	B(int a, int ref)
		:_aobj(a)
		, _ref(ref)
		, _n(10)
	{}
private:
	A _aobj; // 没有默认构造函数
	int& _ref; // 引用
	const int _n; // const 
};

1.3 explicit关键字

对于接受单个参数的构造函数,不仅具有初始化的功能,还具有隐式转换的功能

下面这三种都属于只接受单个参数的:

1、构造函数只有一个参数 2、构造函数有多个参数,但是只有一个没有默认值 3、全缺省构造函数

代码语言:javascript复制
class Date
{
public:
	// 1. 单参构造函数,没有使用explicit修饰,具有类型转换作用
	// explicit修饰构造函数,禁止类型转换---explicit去掉之后,代码可以通过编译
	explicit Date(int year)
		:_year(year)
	{}
	/*
	// 2. 虽然有多个参数,但是创建对象时后两个参数可以不传递,没有使用explicit修饰,具有类型转
   换作用
	// explicit修饰构造函数,禁止类型转换
	explicit Date(int year, int month = 1, int day = 1)
	: _year(year)
	, _month(month)
	, _day(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;
};
void Test()
{
	Date d1(2022);
	// 用一个整形变量给日期类型对象赋值
	// 实际编译器背后会用2023构造一个无名对象,最后用无名对象给d1对象进行赋值
	d1 = 2023;
	// 将1屏蔽掉,2放开时则编译失败,因为explicit修饰构造函数,禁止了单参构造函数类型转换的作
//	用
}

二、static成员

static修饰的成员变量就是类中的静态成员变量,static修饰的成员函数就是静态成员函数

我们需要注意的是静态成员变量一定要在类外进行初始化

代码语言:javascript复制
class A
{
private:
	static int _a;
};

//在类外对_a进行初始化
int A::_a = 0;

int main()
{
	return 0;
}

注意事项:

1、静态成员存放在静态区,为所有类成员共享 2、静态成员必须在类外定义(初始化),类中只是声明 3、静态成员访问方式:类名::静态成员或者对象.静态成员

三、友元

友元是一种突破类封装的一种方法,在特定的情况下使用会有意想不到的好处,但是类存在的意义就是进行封装,所以友元的使用会破坏耦合性,所以我们平时要尽量减少使用友元

友元有两种:友元类和友元函数

3.1 友元函数

对于一些函数而言,并不适合将它封装在类中,比如operator<<流输出函数

我们一般是这样用这个函数的:

代码语言:javascript复制
int main()
{
	cout << d1 << endl;
	return 0;
}

而在将这个函数写在类中的时候,由于类中函数this指针默认的是第一个操作数(也就是左操作数),所以这样输出是不行的

需要这样才能保证:

代码语言:javascript复制
int main()
{
	d1 << cout;
	//或者
	d1.cout;
	return 0;
}

但这样又违背常理,所以我们往往就需要在类外写一个全局函数来实现这个功能,但是在类外的话就不能直接访问类中的私有化成员,所以就有了友元函数的出现

友元函数实现上述功能的操作如下:

代码语言:javascript复制
class Date
{
	friend ostream& operator<<(ostream& _cout, const Date& d);
	friend istream& operator>>(istream& _cin, Date& d);
public:
	Date(int year = 1900, 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;
	return _cout;
}
istream& operator>>(istream& _cin, Date& d)
{
	_cin >> d._year;
	_cin >> d._month;
	_cin >> d._day;
	return _cin;
}
int main()
{
	Date d;
	cin >> d;
	cout << d << endl;
	return 0;
}

3.2 友元类

友元类跟上面的友元函数很想,也是间接访问一个类中私有化成员的一种方法

比如一个时间类Time和一个日期类Date,我们就可以通过在时间类中声明日期类为时间类的友元类从而直接访问时间类中的私有成员变量

代码语言:javascript复制
class Time
{
	friend class Date;     //声明Date为友元类
private:
	int _hour;
	int _min;
};
class Date
{
public:
	void test(int hour, int min)
	{
		_t._hour = hour;       //可以在Date类中直接引用Time类私有成员变量
		_t._min = min;
	}
private:
	int _year;
	int _month;
	int _day;
	Time _t;
};

四、内部类

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

重点:

1、内部类和外部类实际上还是两个完全独立的类,完全可以将它们两个分开写 2、内部类是外部类的友元类,可以访问外部类的私有成员,但外部类不是内部类的友元类 3、内部类可以直接访问外部类的静态成员,不需要外部类的对象名或者类名

例如:

代码语言:javascript复制
class A
{
public:
	class B
	{
	public:
		void Test(const A& a)
		{
			cout << k << endl;    //静态变量可以直接访问
			cout << a.h << endl;
		}
	};
private:
	static int k;
	int h;
};
int A::k = 1;
int main()
{
	A::B b;
	b.Test(A());
	return 0;
}

运行结果:

五、总结

以上就是C 类与对象的全部内容了,这三期基本就把类与对象所有知识点全部囊括了,如果有不理解的地方,欢迎在评论区中指出或者与我私信交流!!!

感谢各位大佬观看,创作不易,还请各位大佬点赞支持!!!

0 人点赞