C++Primer 学习(类 一)类的基础_内置的赋值运算符把它的左侧运算对象当成左值返回(1)

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

今天学习的是类这一章的第一部分,本部分主要从以下几个知识点:

1 定义抽象数据类型

  1. 引入 this
  2. 引入const成员函数
  3. 类作用域和成员函数
  4. 定义一个返回this对象的函数

2. 定义类相关的非成员函数

3. 构造函数

  1. 合成的默认构造函数
  2. =default 的含义
  3. 构造函数初始值列表

4. 拷贝、赋值和析构

  1. 某些类不能依赖于合成的版本

**类的基本思想是数据抽象(data abstraction)和封装(encapsulation)。**数据抽象是一种依赖于接口(interface)和实现(implementation)分离的编程(以及设计)技术。

类的接口包括用户所能执行的操作;类的实现则包括类的数据成员、负责接口实现的函数体以及定义类所需的各种私有函数。封装实现了类的接口和实现的分离。

封装后的类隐藏了它的实现细节,也就是说,类的用户只能使用接口而无法访问实现部分。

类要想实现数据抽象和封装,需要首先定义一个抽象数据类型(abstract data type)。在抽象数据类型中,由类的设计者负责考虑类的实现过程;使用该类的程序员则只需要抽象地思考类型做了什么,而无须了解类型的工作细节。

1 定义抽象数据类型

定义在类内部的函数是隐式的inline函数。

1.1 引入 this

让我们再一次观察对isbn成员函数的调用:

total.isbn ()

在这里,我们使用了点运算符来访问total对象的isbn成员,然后调用它。隐式地返回 total.bookNo。

**成员函数通过一个名为this的额外的隐式参数来访问调用它的那个对象。**当我们调用一个成员函数时,用请求该函数的对象地址初始化this。例如,如果调用total.isbn ()则编译器负责把total的地址传递给isbn的隐式形参this,可以等价地认为编译器将该调用重写成了如下的形式:

//伪代码,用于说明调用成员函数的实际执行过程
Sales_data: :isbn (&total)

其中,调用Sales data的isbn成员时传入了total的地址。在成员函数内部,我们可以直接使用调用该函数的对象的成员,而无须通过成员访问运算符来做到这一点,因为this所指的正是这个对象。

**任何对类成员的直接访问都被看作this的隐式引用。**我们也可以在成员函数体内部使用this,因此尽管没有必要,但我们还是能把isbn定义成如下的形式:

std::string isbn () const {return this->bookNo;}

因为this的目的总是指向“这个”对象,所以this是一个常量指针,我们不允许改变this中保存的地址。

1.2 引入const成员函数

isbn函数的另一个关键之处是紧随参数列表之后的const关键字,const的作用是修改隐式this指针的类型。默认情况下,this的类型是指向类类型非常量版本的常量指针。例如在Sales data成员函数中,this的类型是Sales data *const。

尽管this是隐式的,但它仍然需要遵循初始化规则,因此在默认情况下我们不能把this绑定到一个常量对象上。这一情况也就使得我们不能在一个常量对象上调用普通的成员函数。

如果isbn是一个普通函数而且this是一个普通的指针参数,则我们应该把this声明成 const Sales data *const。毕竞,在isbn的函数体内不会改变this所指的对象,所以把this设置为指向常量的指针有助于提高函数的灵活性。

然而,this是隐式的并且不会出现在参数列表中,所以在哪儿将this声明成指向常量的指针就成为我们必须面对的问题。

**C++语言的做法是允许把const关键字放在成员函数的参数列表之后,,此时,紧跟在参数列表后面的const表示this是一个指向常量的指针。**像这样使用const的成员函数被称作常量成员函数(const member function)。可以把isbn的函数体想象成如下的形式:

//伪代码,说明隐式的this指针是如何使用的
//下面的代码是非法的:因为我们不能显式地定义自己的this指针
//谨记此处的this是一个指向常量的指针,因为isbn是一个常量成员
std: :string Sales_data::isbn (const Sales_data *const this)
{
 return this->isbn; 
}

因为this是指向常量的指针,所以常量成员函数不能改变调用它的对象的内容。在上例中,isbn可以读取调用它的对象的数据成员,但是不能写入新值。注意:常量对象,以及常量对象的引用或指针都只能调用常量成员函数。

1.3 类作用域和成员函数

类本身就是一个作用域。类的成员函数的定义嵌套在类的作用域之内,因此, isbn中用到的名字bookNo其实就是定义在Sales data内的数据成员。值得注意的是,即使bookNo定义在isbn之后,isbn也还是能够使用bookNo。

**因为:编译器分两步处理类:首先编译成员的声明,然后才轮到成员函数体。**因此,成员函数体可以随意使用类中的其他成员而无须在意这些成员出现的次序。

1.4 定义一个返回this对象的函数

函数combine的设计初衷类似于复合赋值运算符+=,调用该函数的对象代表的是赋值运算符左侧的运算对象,右侧运算对象则通过显式的实参被传入函数:

Sales_data& Sales_data::combine (const Sales_data &rhs)
{
  units_sold += rhs.units_sold; //把rhs的成员加到this对象的成员上
  revenue += rhs. revenue;
  return *this;//返回调用该函数的对象
}

total. combine (trans);//更新变量total当前的值

total的地址被绑定到隐式的this参数上,,而rhs绑定到了trans上。因此,当combine执行下面的语句时:

units_sold += rhs.units_sold; //把rhs的成员添加到this对象的成员中

效果等同于求total.units sold和trans.unit sold的和,然后把结果保存到total.units sold中。

**该函数一个值得关注的部分是它的返回类型和返回语句。**一般来说,当我们定义的函数类似于某个内置运算符时,应该令该函数的行为尽量模仿这个运算符。内置的赋值运算符把它的左侧运算对象当成左值返回,因此为了与它保持一致,combine函数必须返回引用类型。因为此时的左侧运算对象是一个Sales data的对象,所以返回类型应该是Sales_data&

如前所述,我们无须使用隐式的this指针访问函数调用者的某个具体成员,而是需要把调用函数的对象当成一个整体来访问:

return *this;//返回调用该函数的对象

其中,return语句解引用this指针以获得执行该函数的对象,也就是说:上面的这个调用返回total的引用。

2. 定义类相关的非成员函数

类的作者常常需要定义一些辅助函数,比如add、read和print等。尽管这些函数定义的操作从概念上来说属于类的接口的组成部分,但它们实际上并不属于类本身。我们定义非成员函数的方式与定义其他函数一样,通常把函数的声明和定义分离开来。如果函数在概念上属于类但是不定义在类中,则它一般应与类声明(而非定义)在同一个头文件内。在这种方式下,用户使用接口的任何部分都只需要引入一个文件。

一般来说,如果非成员函数是类接口的组成部分,则这些函数的声明应该与类在同一个头文件内。

3. 构造函数

每个类都分别定义了它的对象被初始化的方式,类通过一个或几个特殊的成员函数来控制其对象的初始化过程,这些函数叫做构造函数(constructor)。构造函数的任务是初始化类对象的数据成员,无论何时只要类的对象被创建,就会执行构造函数。

构造函数的名字和类名相同和其他函数不一样的是,构造函数没有返回类型;除此之外类似于其他的函数,构造函数也有一个(可能为空的)参数列表和一个(可能为空的)函数体。

类可以包含多个构造函数,和其他重载函数差不多,不同的构造函数之间必须在参数数量或参数类型上有所区别。不同于其他成员函数,构造函数不能被声明成const的。当我们创建类的一个const对象时,直到构造函数完成初始化过程,对象才能真正取得其“常量”属性。因此,构造函数在const对象的构造过程中可以向其写值。

3.1 合成的默认构造函数

但是之前的Sales data类并没有定义任何构造函数,可是之前使用了Sales data对象的程序仍然可以正确地编译和运行。比如定义了两个对象:

Sales_data total;//保存当前求和结果的变量


![img](https://img-blog.csdnimg.cn/img_convert/8ed9efc63b9ad9b414c43c5c48a1dbd9.png)
![img](https://img-blog.csdnimg.cn/img_convert/a1052c9c4d9f4a8319d225e03261ac5c.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618668825)**

715735273759)]

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618668825)**

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值