揭开c++类和对象的奥秘(一)!!!

    前言:

上一篇文章中我们已经初步解了c++为了弥补c语言的不足而添加的一些基础语法,如缺省参数、函数重载、命名空间等等,这些语法会渗透在c++学习的方方面面,也是为我们对c++的深入了解打下了基础。在本篇文章中就让我们一起来学习类和对象的相关知识。

作战地图如下:

    初步认识类(class):

  首先要知道,c语言中我们学过的结构体struct在c++中也升级成了类(毕竟c++要兼容c语言),因此可以借着struct来认识c++中class这一新的关键字。

  和struct类似,class也可以定义抽象的事物,比如一个可能由姓名(字符串)、学号(字符串)、身高(整型)和体重(整形)等特征组成的学生,这就和学习c语言的struct时一样。

  但c++的类除了能定义这些基本特征外还可以定义这个类的“行为”(就是函数),而把数据和函数放在一块的好处我们之后会不断的认识到。

  既然struct在c++中升级成了类,也就能定义函数了,那是否就和c++专属的class一摸一样了呢?其实差别依然存在:c++中引入了成员访问限定符的概念,即形成了c++的特性又兼容了c语言,如下图:

需要注意以下几点:

  1.  c++中类的基本定义格式与c语言的struct类似({}结尾的 ; 千万别忘加了!!!),只不过多了访问限定符成员函数;
  2. c++类的访问限定符分为public(类内和外都可以访问)、private(只有类内可以访问)和protected三种,当前先认为protected和private一样;
  3. c++定义在类内部的函数默认是内联函数

    类的实例化(创建对象):

首先看一个示例:

  1.  类本身相当于一张建筑蓝图,通过一张图可以建造很多间房子----通过一个类可以创建很多个对象,也就是说类和对象是一对多的关系。
  2. 创建具体的对象要申请空间,具体申请多少和c语言的结构体大小的计算类似,具体细节可以看这篇文章中关于结构体内存对齐的内容:写文章-CSDN创作中心。其中不包括成员函数的大小,至于为什么,看完下面有关this指针的内容就明白了。

    this指针:

  刚才我们说到在类中定义的函数不和成员变量存在一起,其实是因为函数代表了一种行为或者逻辑,创建的每一个对象都可以使用。

  倘若每创建一个对象就要多为函数开辟一块空间,势必会导致空间的浪费,因此类中定义的函数存储在代码段中为同一个类的对象共同使用。

  将对象和函数链接起来的是this指针,this指针是调用函数的对象的地址。this指针有如下注意点:

  1. this指针作为调用函数的对象的地址,自动作为函数的第一个形参传递,但不能自己显式的传。
  2. this指针在函数体内可以使用(之后会见到)。

  

     六个默认成员函数:

  出于各种考量,c++中加入的六个默认成员函数,他们的共同特点是我们自己不实现的话编译器会自动生成自动调用,当然,等学完后我们会发现大多情况下还得自己实现,因此默认成员函数最最重要的作用还是自动调用。接下来咱们一起来学习

    一、构造函数:

  构造函数中虽然有构造二字, 却不是创建变量,而是对成员变量进行初始化的函数。变量的创建在建立函数栈帧时就根据内存对齐的原则开辟内存完成了的,构造函数只是对已有的变量赋一个初始值。

    1.构造函数的基本形式:

    2.默认构造函数的定义如下:

注意:这里的"默认"代表可以自动调用,且如果自己定义了构造函数,却不是以下三种,编译器会认为没有默认构造函数可用,从而报错

    3.系统自动生成的构造函数的功能逻辑:

    4.细节补充:

     二、析构函数:

  析构函数就是完成对对象空间释放功能的函数,和构造函数的功能逻辑上相反(不能完全说相反是因为构造函数是对已占空间的变量赋初始值,而构造函数是对对象所占空间的释放)。

   1.析构函数的基本形式:

   2.默认的析构函数:

  因为析构函数没有参数,就不像构造函数那样可以重载出多种形式,所以只用考虑自己显示实现的或者没有显示实现编译器自动生成的这两种。

     3.显示实现析构函数的情景:

  析构函数是否需要自己显式实现取决于是否涉及成员变量是否涉及动态申请的资源,如下图:

        4.细节补充:

  • 规定一个类只能有一个析构函数(像是废话哈哈哈哈哈)
  • 规定同一个类中的成员,后定义的先析构



 

    三、拷贝构造函数:

1.拷贝构造函数存在的必要性(解决传值传参的风险):  

  学习c语言时我们知道,将自定义类型的变量作为参数传值传参时编译器会将变量的内容拷贝一份再传递,这样会造成空间和时间的消耗,所以通常使用结构体的指针作为参数传递。

  但在c语言中传值传参也仅仅是会是程序效率变低,而在c++中由于析构函数的引入,有时自定义类型的变量传值传参就有了风险,下面进行举例:

    2.拷贝构造函数的基本格式和执行逻辑:

    知道了拷贝构造函数是构造函数的重载形式,所以写法上有相似之处,最大的不同之处在于拷贝构造函数的第一个参数必须是对当前类对象的引用,至于原因后面解释,咱先来了解基本的写法和逻辑(因为对象调用非静态成员函数时会将自己的地址(this)作为形参隐式的传递,所以为了方便理解,下面在函数体内就显式的写出this指针)

         3.拷贝构造函数第一个参数必须是当前类类型的引用的原因(避免无限次调用拷贝构造函数):

  第一点讨论构造函存在的必要性时提到了涉及动态申请空间的_arr成员变量,正是因为传统c语言的传值拷贝只是简单的拷贝变量本身的值,但_arr还是指向同一块空间,存在风险。

   所以 c++死死的规定了只要是传值传参就一定会调用拷贝构造函数,此时如果拷贝构造函数也是传值传参而不是引用,那编译器会一视同仁的把拷贝构造函数当作普通的函数,又调用拷贝构造函数的拷贝构造函数,形成无穷递归。文字的阐述太单薄,下面画图解释:

    如果是拷贝构造函数是正确的,也就是传递了当前类类型对象的引用,就会是如下图正确的拷贝逻辑:

    四、运算符重载 

  c/c++的运算符正常情况下只支持对内置类型的操作,而对于自定义类型就无可奈何了。可我们尝尝又有这样的需求:比如两个日期类相减会得到他们相差多少天。像这样有意义的需求就需要通过运算符的重载来实现了。

  注:函数重载是指参数不同的同名函数,而运算符重载是对原生运算符的重新定义,他俩没有关系。

  1.运算符重载的使用规则:

  • operator是表示运算符重载的关键字。
  • 基本形式 :(返回值类型)  operator(要重载的运算符)(函数的参数列表)
  • 不能重载不存在的运算符 ,如 #;
  • 有五个不能重载的运算符:.(成员访问操作符)、.* (不知道什么符) 、sizeof(计算类型大小的操作符) 、? :(条件操作符)、::(域作用限定符)

    2.运算符重载的注意事项:

  • 不要使用无意义的运算符重载(比如重载两个日期相加)。
  • 运算符重载函数的参数要和实际的操作数匹配(如果重载为成员函数默认有一个参数this指针,表示调用这个函数的对象的地址)。
  • 重载后不应改变操作符原来的优先级和结合性。
  • 操作数至少有一个是类类型(即自定义类型,这也是为了防止原生运算符的含义被改变。比如int operator+(int a,int b),此时如果把+重载成-就会出问题)

五、赋值运算符重载:

    1.特殊性:

  同样是运算符的重载,为啥要单拎出赋值运算符 ' = ' 呢?因为他和拷贝构造函数都是把一个对象的值赋给另一个对象,容易使人在概念上模糊不清,但他们的区别可以这样理解:拷贝构造函数是用已存在的对象去构造一个还未存在的新对象,赋值运算符重载是两个已存在对象间的赋值

    2.赋值运算符重载和拷贝构造的对比:

  

    3.赋值运算符重载函数的特点:

  • 语法上规定这个函数必须在类里面实现,因为无论在类里实现后编译器就不会自动生成,而定义在类外就会和编译器自动生成的产生冲突。
  • 如果没有自己实现赋值运算符重载函数,编译器默认生成的对内置类型会完成逐字节的赋值(浅拷贝),对自定义类型的变量会调用它的默认拷贝构造函数。
  • 对于成员变量不涉及动态申请空间时 (如全是普通整形的日期类),编译器默认生成的就够用了;对于成员变量涉及动态申请空间时(如包含了会动态增减的数组的栈时)就需要自己实现并完成深拷贝。
  • 判断是否要自己实现的小技巧:如果一个类的成员变量涉及动态资源的申请且显式实现了析构函数则要自己实现,反之则不用。

    六、取地址运算符重载:

&这个操作符大多情况下不用自己实现,编译器自动生成的完全够用。除非像下面这中情况:

    初始化列表(对构造函数的补充):

  初始化列表的必要性

初始化列表是成员变量初始化的地方,无论是否实现成员变量都要通过初始化列表来获得初始值。这点可以通过以下特例来证明:

  初始化列表的写法:

   初始化列表的规范书写:

 c++规定成员变量初始化的顺序和声明顺序相同,因此建议初始化的顺序和声明的顺序保持一致,否则可能出现以下错误:

     c++11引入的新语法: 

在c++11中,规定成员变量在声明时可以给缺省值,但并不是对变量的初始化,而是和函数的缺省参数类似:它用于给在初始化列表阶段没有初始化的成员变量一个初始值,也就是说,这个缺省值是针对初始化列表的。

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值