【C++】类和对象(下篇)

目录

1、再谈构造函数

1.1 函数体内赋值

1.2 初始化列表

 1.3 explicit关键字

2、static成员

2.1 概念

2.2 特性

3、友元

3.1 友元函数

3.2 友元类

4、内部类

5、再次理解面向对象

6、匿名对象

1、再谈构造函数

1.1 函数体内赋值

在前面我们学到的构造函数中,都是在定义对象时,将成员变量先定义出来,然后再赋值给他们

但若是类中的成员变量比较特殊,像引用、const、自定义类型(且该类没有默认构造函数),若有这三者其一,都是必须在定义的同时就赋值,不能像上面一样先定义,然后再赋值

1.2 初始化列表

上面的这种构造函数称为函数体内赋值,为了适应这三种情况,下面的这种构造函数成为初始化列表

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

2、类中包含以下的成员变量,必须放在初始化列表中初始化:

     a. 引用成员变量

     b. const成员变量

     c. 自定义类型成员变量(且该类没有默认构造函数时)

原因:引用和const都必须在定义时就初始化

           若对类进行初始化,那么必然会去调用类的构造函数,而若这个类没有默认的构造函数,也就是说要取调用这个类的构造函数,必须传参(前面也有说过这种情况下使用之前的函数体内赋值会报错),所以必须使用初始化列表来传一个值过去(也不一定是一个值,要看这个类的构造函数需要几个参数),让其能够调用类中的构造函数

当然,初始化列表也是最好使用全缺省 

成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关,实际中,建议声明和定义的顺序写成一样,这样不容易出错

答案是D,因为在类中,是先定义了_a2,所以初始化列表中也会先初始化_a2

 1.3 explicit关键字

构造函数不仅可以构造与初始化对象,对于接收单个参数的构造函数,还具有类型转换的作用。接收单个参数的构造函数具体表现:
1. 构造函数只有一个参数
2. 构造函数有多个参数,除第一个参数没有默认值外,其余参数都有默认值
3. 全缺省构造函数
C++98在类型转换时,只支持构造函数只有一个参数的情况

若想关闭掉这个隐式类型转换的功能,可以在构造函数前加explicit

在C++11中,加入了针对构造函数有多个参数的情况下的做法

在构造函数前加上explicit的效果是一样的

2、static成员

2.1 概念

 声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的成员函数,称之为静态成员函数。静态成员变量一定要在类外进行初始化(因为静态成员变量在程序刚开始运行时就有了,也就是对象还没创建时就有了,所以要在类外全局的地方定义,不能在构造函数中定义,构造函数是创建对象时才调用)

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

解题关键:对象一定是由构造函数或拷贝构造产生的

 

此时会有一个问题,就是谁都可以修改n的值(缺少封装)

若将n定义为成员变量,那么此时那个对象调用了构造函数或拷贝构造,++的就是这个对象中的n

所以应该将n定义为成员变量,且前面加上static

但是此时依然不是很完美,因为获取n并不方便,所以可以将GetN也定义成static,这样可以直接A::GetN()访问n,不用再通过对象(原因是static成员函数没有this指针,所以不使用对象就可以直接调用),也正因为static没有this指针,所以不能访问非静态的成员(成员函数和成员变量)

2.2 特性

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

静态的成员变量不能在定义时就给缺省值

3、友元

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

3.1 友元函数

友元函数就是在类外面可以访问类中的private成员的函数

此时会发现f1函数会报错,因为d是Date类的对象,Date类中的成员变量是private的,而f1去访问了private的对象,所以报错了。这个时候的解决办法可以是将f1定义在类中,使之变成成员函数,另一种方法就是定义为友元函数

加friend后变成了友元函数,不受访问限定符的限制

访问限定符的用处

了解一点背景知识

cout是iostream中定义的一个全局类型的对象,类型是ostream

cin是iostream中定义的一个全局类型的对象,类型是istream

'<<'输出运算符,'>>'输入运算符

现在重载这两个运算符,让自定义类型不用去调用类中的Print函数就可以完成输出

 此时会发现3种写法中,前两种是可以的,但是前两种的可读性都太差了,为什么第三种不行呢?

因为类中的成员函数会默认左边操作对象为this指针, 所以这里d1要放在左边,但是这样可读性太差,所以此时可以使用友元函数

此时会发现没办法连续输出,因为没有返回值,应当cout<<d1后,返回一个cout,就可以输出d2

再来实现">>"

cout和cin能自动识别类型,是因为函数重载

int x = 1;

double y = 1.11;

cout<<x; // cout.operator(&cout , x);

cout<<y; // cout.operator(&cout , y); //&cout就是this

1. 友元函数可访问类的私有和保护成员,但不是类的成员函数
2. 友元函数不能用const修饰(因为没有this,这里说的this是指加在函数最后面的那个this,这个this都是用来修饰this指针的)
3. 友元函数可以在类定义的任何地方声明,不受类访问限定符限制
4. 一个函数可以是多个类的友元函数
5. 友元函数的调用与普通函数的调用原理相同 

3.2 友元类

某个类想要访问另一个类 

4、内部类

概念:如果一个类定义在另一个类的内部,这个内部类就叫做内部类。内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越的访问权限。
注意:内部类就是外部类的友元类,参见友元类的定义,内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元。
特性:
1. 内部类可以定义在外部类的public、protected、private都是可以的。
2. 注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名。
3. sizeof(外部类)=外部类,和内部类没有任何关系。

5、再次理解面向对象

现实生活中的实体计算机并不认识,计算机只认识二进制格式的数据。如果想要让计算机认识现实生活中的实体,用户必须通过某种面向对象的语言,对实体进行描述,然后通过编写程序,创建对象后计算机才可以认识。比如想要让计算机认识洗衣机,就需要:
1. 用户先要对现实中洗衣机实体进行抽象---即在人为思想层面对洗衣机进行认识,洗衣机有什么属性,有那些功能,即对洗衣机进行抽象认知的一个过程
2. 经过1之后,在人的头脑中已经对洗衣机有了一个清醒的认识,只不过此时计算机还不清楚,想要让计算机识别人想象中的洗衣机,就需要人通过某种面相对象的语言(比如:C++、Java、Python等)将洗衣机用类来进行描述,并输入到计算机中
3. 经过2之后,在计算机中就有了一个洗衣机类,但是洗衣机类只是站在计算机的角度对洗衣机对象进行描述的,通过洗衣机类,可以实例化出一个个具体的洗衣机对象,此时计算机才能洗衣机是什么东西。
4. 用户就可以借助计算机中洗衣机对象,来模拟现实中的洗衣机实体了。在类和对象阶段,大家一定要体会到,类是对某一类实体(对象)来进行描述的,描述该对象具有那些属性,那些方法,描述完成后就形成了一种新的自定义类型,才用该自定义类型就可以实例化具体的对象。

C++还追求高内聚、低耦合

低耦合:类与类之间的关系越少越好

高内聚:一个类里面联系的越紧密越好

6、匿名对象

通常,我们要去调用类中的函数,会去创建对象。但是如果只是需要调用一次这个函数,去创建一个对象未免有些太麻烦了,这时候可以创建匿名对象,匿名对象。

 

 

相关推荐

  1. C++ 对象

    2024-04-08 22:22:02       9 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-08 22:22:02       19 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-08 22:22:02       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-08 22:22:02       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-08 22:22:02       20 阅读

热门阅读

  1. 【阅读笔记】《同意》

    2024-04-08 22:22:02       13 阅读
  2. 详细介绍下PYTHON API的用法

    2024-04-08 22:22:02       16 阅读
  3. [TS面试]TS中类型的全局声明与局部声明?

    2024-04-08 22:22:02       14 阅读
  4. 我的项目笔记

    2024-04-08 22:22:02       14 阅读
  5. C++学习笔记九--模版

    2024-04-08 22:22:02       13 阅读
  6. Linux——gcc

    2024-04-08 22:22:02       15 阅读
  7. 【LeetCode热题100】33. 搜索旋转排序数组(二分)

    2024-04-08 22:22:02       11 阅读
  8. Flutter 单例模式的多种实现方法与使用场景分析

    2024-04-08 22:22:02       13 阅读
  9. 为什么要在 Golang 中使用指针

    2024-04-08 22:22:02       15 阅读
  10. 从零开始实现一个RPC框架(三)

    2024-04-08 22:22:02       8 阅读