C++类与对象(下)

目录

再谈构造函数

构造函数体赋值

初始化列表

explicit关键字

static成员

静态成员变量

静态成员函数

友元

友元函数

友元类


再谈构造函数

构造函数体赋值

构造函数体中的语句只能将其称作为赋初值,而不能称作初始化。因为初始化只能初始化一次,而构造函数体内 可以多次赋值。
如何证明?如下日期类中,编译代码后,直接报错说引用类型变量r未初始化,这就说明构造函数中做的工作只是赋值,而不是初始化。

初始化列表

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

如下代码中,在初始化列表中对引用类型的变量r进行初始化,此时代码编译就可以通过了。

注意以下几点:

1. 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
2. 类中包含以下成员,必须放在初始化列表位置进行初始化:引用成员变量、const成员变量、自定义类型成员(该类没有默认构造函数)
引用类型的成员变量前面已经举例了,下面来看const成员变量,将下图中左边代码进行修改,就可以编译通过,证明了const成员必须在初始化列表中尽心初始化。

 那么对于没有默认构造函数的自定义类型成员又是怎么一回事?

 为什么编译会报错呢?因为在Date类中,如果自己没有写初始化列表,编译器就会自动生成,对于基本类型,会初始化为随机值,但是对于自定义类型对象_a,编译器会这样处理:_a();这就相当于调用了A类中默认构造函数,但是此时A类中并没有默认构造函数,就会报错。解决方案:在A类中给出默认构造函数或者在Date类初始化列表中手动对_a进行初始化。

3. 尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化。
4. 成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关

explicit关键字

构造函数不仅可以构造与初始化对象,对于单个参数的构造函数,还具有类型转换的作用
如下代码中用整型元素给类类型对象赋值,可以成功编译与运行:
#include <iostream>
using std::cout;
class Date{
public:
	Date(int year)
		:_year(year)
	    ,_month(3)
	    ,_day(25)
	{
		cout << "Date(int year)" <<this<< '\n';
	}
	~Date(){
		cout << "~Date()" <<this<< '\n';
	}
private:
	int _year;
	int _month;
	int _day;
};
int main(){
	Date d(2022);
	//2023是int类型的,不能直接给Date类型的对象d进行直接赋值
	//编译器在编译的时候,发现Date有一个单参的构造函数
	//于是编译器借助单参的构造函数生成一个临时的对象,再用这个临时对象给d赋值
	d = 2023;
	return 0;
}

运行结果如下:

 根据打印结果,首先调用了构造函数对d进行初始化,然后生成了临时变量给d赋值,在对这个临时变量进行析构。

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

static成员

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

静态成员变量

特性:

1.静态成员变量为所有类对象所共享,不属于某个具体的实例,不影响sizeof(对象)的大小

2. 静态成员变量必须在类外定义,定义时不添加static关键字

3.静态成员变量即可用类名::静态成员或者对象.静态成员来访问

4.静态成员变量不能放在构造函数初始化列表的位置进行初始化

5.静态成员变量访问受到访问限定符的限定

面试题:实实现一个类,计算程序中创建了多少类对象。
这个题使用全局变量作为计数器可以解决,但缺点是此全局变量在任何地方都可以访问,不够安全。使用类静态成员作为计数器就可以解决此问题:
#include <iostream>
using std::cout;
class Date{
public:
	Date(int year,int month,int day){
		_year = year;
		_month = month;
		_day = day;
		count++;
	}
	Date(Date& d){
		_year = d._year;
		_month = d._month;
		_day = d._day;
		count++;
	}
	~Date(){
		count--;
	}
	int GetCount(){
		return count;
	}
private:
	int _year;
	int _month;
	int _day;
	static int count;//只是声明
};
int Date::count = 0;//定义
int main(){
	Date d1(2022,3,25);
	Date d2 = d1;//注意:此处调用的也是拷贝构造
	Date d3(d2);
	cout << d3.GetCount()<< '\n'; 
	return 0;
}
编译运行后:

打印出来了3,说明创建了三个对象,与实际情况符合。


静态成员函数

特性:

1.静态成员函数中没有隐藏的this指针

2.静态成员函数即可用类名::静态成员或者对象.静态成员来访问

3.静态成员函数中不能访问普通成员变量和普通成员方法,只能访问静态成员

友元

友元函数

友元函数可以直接访问类的私有成员,它是定义在类外部普通函数,不属于任何类,但需要在类的内部声 明,声明时需要加friend关键字。
如下代码中,重载了输出流运算符,在类中用friend声明后,直接在重载的函数中访问类的私有成员,编译运行成功。

特性:

1.友元函数可访问类的私有和保护成员,但不是类的成员函数

2.友元函数不能用const修饰

3.友元函数可以在类定义的任何地方声明,不受类访问限定符限制

4.一个函数可以是多个类的友元函数
5.友元函数的调用与普通函数的调用和原理相同

友元类

友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
如下代码,在Date类中可以直接访问A类的私有成员:
#include <iostream>
using namespace std;
class A{
public:
	friend class Date;//声明Date类为A类的友元类
	A(int a){
		_a = a;
	}
private:
	int _a;
};
class Date{
public:
	Date(int year, int month, int day)
		:Aa(10)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print(){
		cout << _year <<' '<< _month <<' '<< _day<<' ' << Aa._a;//可以访问Aa类的私有成
	}
private:
	int _year;
	int _month;
	int _day;
	A Aa;
};
int main(){
	Date d1(2022,3,25);
	d1.Print();
	return 0;
}

特性:

1.友元关系是单向的,不具有交换性。
2.友元关系不能传递
如果B是A的友元,C是B的友元,则不能说明C时A的友元
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值