C++类和对象(下)

目录

一、初始化列表

二、单参构造参数和explicit关键字

三、匿名对象

四、static成员 

五、友元 

六、内部类


一、初始化列表

之前我们在构造函数中写得还不错,也没发现什么问题,为什么C++还有搞一个初始化列表呢?

如下这段代码,对于const类型和引用,他们必须在初始化的时候就被定义,而我们想在构造函数这个 {} 里面去初始化是不可行的。

C++引入了初始化列表来帮助我们完成操作 

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

初始化列表式每个成员定义的地方,不管你写不写,每个成员都要走初始化列表。 

1. 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)

2. 类中包含以下成员,必须放在初始化列表位置进行初始化:

  • 引用成员变量
  • const成员变量
  • 自定义类型成员(且该类没有默认构造函数时)

C++11中,还支持成员声明的地方给值,这个值是缺省值,缺省值会给到初始化列表,初始化列表没有显示给值,就会用这个缺省值。如下

注意:初始化列表的初始化顺序跟成员声明的顺序有关,跟列表顺序无关 

 第一步先初始化了_a

第二部是_b 

 最后才是_c

 对于我们后续开发来讲,尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量, 一定会先使用初始化列表初始化。

二、单参构造参数和explicit关键字

构造函数不仅可以构造与初始化对象,对于单个参数或者除第一个参数无默认值其余均有默认值 的构造函数,还具有类型转换的作用。

举例子,下面的代码竟然可以将一个参数直接赋值给类对象,这实际上也是构造函数的功劳,因为构造函数只有单个参数。

对于43行代码,编译器会自动用  2023  构造出一个临时的类对象(类型转换),再将这个类对象通过默认赋值重载给到d1。

这只适用于单个参数或者除第一个参数无默认值其余均有默认值。

但是C++11添加了新特性,可以支持多个参数。

使用 {}  来进行赋值。这个我们作为了解可以运行即可。

如果给Date类加上explicit关键字,就会禁止单参构造函数类型转化,因此代码就不能通过编译了

三、匿名对象

匿名对象就是没有对象名,并且声明周期只在这一句代码的对象。

 

如果我们不需要这个类对象一直存在,只需短暂使用,那么用匿名对象会更好,占用时间短暂,因为析构很快,并且写起来简洁 。

如下我们可以看到,当这条语句执行完毕后,匿名对象就已经析构了。

四、static成员 

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

如下图所示,static成员想在声明的时候初始化,后面交给初始化列表去操作,是不可以的。

 我们需要放到类外面去定义,如此才不会报错。

我们需要注意,静态成员是所有类对象共有的,他存放在静态区,因此类对象的大小是不算静态成员的。如果有非静态成员,那么类对象大小就全看非静态成员,如果全是静态成员,那么类对象大小就为1,代表站位

 我们的代码思路是构造一个类对象,_a++ ,从代码可以看到,类对象a 和 aa他们的_a都是同一个。这可以作为一个类对象的计算器。

但是这里我们会发现,想要调用Print()函数,就必须要构造出类对象,不管是临时的还是其他,都得构造类对象,但是这样一来,我们的初心就变了,_a 会发生改变,那么有没有什么方法可以让我们能够简便的调用Print()并且不让_a发生改变呢?

这就需要将Print()设置为静态函数了

静态函数只能调用静态的成员变量,非静态的调不动。变成静态函数  _c  就会报错,因为静态函数没有this指针,而为啥静态成员_a能调得动呢?还是因为 _a 是静态变量,所有类成员都共用这一份,因此可以调用。

我们去掉打印 _c 只看 _a,我们可以直接用   类名 :: Print()  去直接打印,不会发生_a++的情况。

五、友元 

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

上一篇文章 日期类实现,为了让日期类能够使用cout输出,我们就使用了友元,因为类函数第一个默认的参数是this指针,下面代码虽然看起来out是第一个,实际上是第二个参数。因此重载后想要运行就得是   d << cout;

ostream& Date::operator<<(ostream& out)

 想要 cout << d,就得写成友元的形式

friend ostream& operator<<(ostream& out, const Date& d);
friend istream& operator>>(istream& in, Date& d);

 友元注意事项

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

友元不仅仅可以是一个函数,还可以是一个类 

如下在Date类里面创建了一个Time类对象,但是你无法访问Time类的私有成员,

但你只需在Time类上添加一个Date类的友元声明,在Date类里面就可以访问到Time类的私有成员了。

友元类注意事项

  • 友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
  • 友元关系是单向的       不具有交换性。 比如上述Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。
  • 友元关系不能传递       如果C是B的友元, B是A的友元,则不能说明C时A的友元。 友元关系不能继承,在继承位置再给大家详细介绍。

六、内部类

内部类的概念很简单   ,如果一个类定义在另一个类的内部,这个内部类就叫做内部类。

如图,B是A的内部类,B类受A类域和访问限定符的限制,其实他们两个是独立的类。

同时要注意,去算A类对象的大小,是不会管A类有没有内部类,依然是看A类的成员变量。 

要想创建B类的类对象,需要通过A类的作用域去找。

内部类就是外部类的友元类

在B类中创建A类对象,可以直接访问到A类的私有成员。

  • 6
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值