【C++】——类和对象(初始列表,Static成员,友元)

本文详细解释了C++中构造函数的初始化列表、赋值的区别,强调了必须初始化的特定类型,并介绍了静态成员和友元的概念,以及它们在类设计中的应用和注意事项。
摘要由CSDN通过智能技术生成

4ea68a990b8b4ad5b754868032eee15d.jpeg

创作不易,多多支持! !😘😘

前言

因为前面的构造函数还有些地方不够清晰,所以这里需要再继续补充一些

一  初始化列表

1.1认识初始化

对于默认的构造函数来说,我们都知道它是起到了初始化的操作,但是它对于内置类型是不做处理的,所以一般初始化都需要我们主动去写,不然就算一堆随机值。下面就来看看构造函数初始化的具体步骤

我们还是拿日期类来说明

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class Date
{
public:
	Date()
	{}
	void Print()
	{
		cout << _year<<'-' << _month << '-' << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
	
};
int main()
{
	Date d;
	d.Print();
	return 0;
}

以上代码就写了一个构造函数,其作用什么都不干,对于打印的结果就是一堆随机值 

347d35ef93a345abb095bb6567839c05.png

那我们改成我们常用的形式看看如何

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class Date
{
public:
	Date()
	{
		_year = 2024;
		_month = 4;
		_day = 27;
	}
	void Print()
	{
		cout << _year<<'-' << _month << '-' << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
	
};
int main()
{
	Date d;
	d.Print();
	return 0;
}

很显然输出的答案就发生了变化

aa508c08c51343ce873143f86497a536.png

说这么多其实就是想表达一个点,这并不是初始化而是赋值

 因为初始化是在初始化列表实现的,函数体里只是赋值,所以这里要分清楚

对于真实性,我们可以调试来看看

6458ab5f8f89442b883fba033b9d0a7e.png

从图中我们可以看到的是,在进入构造函数里时,所有值都已经初始化成了随机值,后面的仅仅只是对这些内置类型赋值而已.

而对于初始化,就是由初始化列表提供的,因为我们没有显示的写出初始化列表,所以编译器就自动给了随机值。

1.2为什么要写初始化列表

对于上面的认识,可能就会觉得,为啥要初始化,直接让编译器初始化为随机值,我们后面再来赋值不就好了,其实不然。

对于以下类型必须得初始化

int& a;//引用

const int b;//const 类型

A   c;//没有默认构造的对象

以上类型必须得初始化,不然就会报错,所以就有初始化列表这个概念

 所有的内置类型都需要经过初始化列表

对于上面三个类型,必须放在初始化列表进行初始化,不然就会报错。

1.3 初始化的形式

那么对于初始化列表的基本形式应该是这样的

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

那对于上面的样例,我们可以照虎画猫有

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class A//A类
{
public:
	A(int a):m(a)
	{}
private:
	int m;
};


class Date//日期类
{
public:
	Date(A c,int& a):_a(a),_c(c),_b(10)
	{
		_year = 2024;
		_month = 4;
		_day = 27;
	}
	void Print()
	{
		cout << _year<<'-' << _month << '-' << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
    //必须初始化的类型
	int& _a;
	const int _b;
	A _c;
};
int main()
{
	A c(1);
	int a = 0;
	Date d(c,a);
	return 0;
}

 注意:

1.所有变量的初始化只进行一次,但是赋值可以多次

2.初始化列表初始化的顺序是按照声明的顺序初始化的

 1.4  explicit 关键字 

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

对于单个参数的构造函数而言

有下面三种情况

1. 构造函数只有一个参数
2. 构造函数有多个参数,除第一个参数没有默认值外,其余参数都有默认值
3. 全缺省构造函数
class Date
{
public:
	 explicit Date(int year,int month=7,int day=21)
	{
		_year = 2024;
		_month = 4;
		_day = 27;
	}
	void Print()
	{
		cout << _year<<'-' << _month << '-' << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1(2024)
	d1 = 2024;//这里其实发生了隐式类型转换
	return 0;
}

上面的代码是编译不过的,因为加了一个  explicit,它是阻止类型转换的。

一个整数赋值给一个对象,那么这个整数先转换成相同类型的对象,然后再赋值。

这里注意,如果有多个参数,上面的就会报错。

二  Static关键字

声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的成员函数,称之为静态成员函数。静态成员变量一定要在类外进行初始化。

private:
	int _year;
	int _month;
	static int _day;//声明
};
int Date::_day = 20;//初始化

其中我们还需要注意的是

1. 静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区
2. 静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明
3. 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问
4. 静态成员函数没有隐藏的this指针,不能访问任何非静态成员
5. 静态成员也是类的成员,受public、protected、private 访问限定符的限制

以上几点注意就行了,特别是第4点。

其实还有一个特例

private:
	int _year;
	int _month;
	const static int _day;//如果加入一个const,那么就可以不在类外面初始化
};

这个只对int类型有效,对于其他类型都是无效的

用static修饰以后,那么这个成员就是所有类共有的,所以可以直接用类名访问。

三  友元

友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多
用。
友元分为:友元函数和友元类

3.1友元函数

友元函数就是在函数名前加上friend

 下面给出一个实例进行说明

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
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;
}

这段代码是对输入输出流的操作符进行了重载,使之可以输入对象,而不单单是内置类型

,那为什么要把这两个函数定义为友元呢?

 如果不定义为友元,那我们就得把这两个函数放到类里面去才能访问到私有的成员变量,那么又有一个问题就来了。

767ea9b58a31456da1bbfb8c287c68c0.png

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

 友员函数不是成员函数,所以不能被const修饰,这一点要注意。

3.2  友元类

1.友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
2.友元关系是单向的,不具有交换性。
4.友元关系不能传递
如果B是A的友元,C是B的友元,则不能说明C时A的友元。

定义了友元类,比如B是A的友元,那么B可以访问A的所有成员,但是A不能访问B的成员,比较自私 

内部类

如果一个类定义在另一个类的内部,这个内部类就叫做内部类。内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越的访问权限。

 其实就是跟友元类是差不多的,只不过友元类打破了封装性,但是内部类使封装更加好。

以上就是关于对构造函数的一些补充还有其他知识点的说明,如果看到这里对你有帮助,那支持一些吧😍😍😍 

3bb809f83d744bf3961a43f5452ba6ab.png

  • 43
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 32
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值