新号别搞:c++类与对象


1.类


1.1 类定义

  • 定义类的关键字class,后+类的名字,{}中为类的主体,结束有;号。
  • {}的内容称为类的成员:类变量称为的属性/成员变量; 函数称为类的⽅法/成员函数 {类里面的成员函数默认为inline}
  • 区分成员变量,在成员变量前⾯或者后⾯加_ 或者 m开头
  • C++中struct也可以定义类,C++兼容C中struct的⽤法,同时struct升级成了类,明显的变化是struct中可以定义函数

1.2 访问限定符

C++实现封装,用类将对象的属性与方法结合在⼀块,通过访问权限选择性的将接⼝

提供给外部的⽤⼾使⽤

  • public修饰的成员在类外可直接被访问;protected、private修饰则在类外不能。
  • 访问权限作⽤域从该访问限定符出现的位置开始直到下⼀个访问限定符出现时为⽌,没有则到 } 即类结束
  • class定义成员没有被访问限定符修饰时默认为private,struct默认为public
  • 成员变量被限制为private/protected,给别⼈使⽤的成员函数为public

1.3 类域

类定义了⼀个新的作用域,放类所有的成

员。

  • 在类体外定义成员时,需要使用 : : 作用域操作符指明成员属于哪个类域
  • 影响编译的查找规则。
    • A函数不指定类域,编译器就把A当成全局函数,那么编译时找不到A的成员的声明/定义会报错。
    • 指定类域,知道A是成员函数,当前域找不到的A的成员,就会到类域中去查找。

2.实例化


2.1 实例化概念

  • ⽤类类型在物理内存中创建对象的过程,称为类实例化.对象类比设计图和实体房子
  • 类是对象进⾏⼀种抽象描述(模型一样),限定了类有哪些成员变量
    • 目前只是声明,没有分配空间,⽤类实例化出对象时,才会分配空间

2.2 对象大小(存储)

  • 成员变量:存储在对象内部。每个实例化的对象都有独立的数据空间,来存放各自的成员变量
  • 成员函数:不存储在对象内部。函数编译后是指令,存放在公共的代码段
  • 没有成员变量的对象大小为1,在位标识对象存在
  • 对象不存函数指针,节省空间,会在编译链接时确定

3. this指针


3.1 this指针定义

编译器编译后,类的成员函数默认都会在形参第⼀个位置,增加⼀个当前类类型的指针

  • 类的成员函数中访问成员变量,本质都是通过this指针访问的。(如this->_year = year;)
  • C++规定不能在实参和形参的位置显⽰的写this指针(编译时编译器会处理),但是可以在函数体内显⽰使⽤this指针。
  • this指针存在内存的栈区

3.2 题目理解


4. 成员函数


4.1 类的默认成员函数

用户没有显式实现,编译器会⾃动⽣成的成员函数

  • 说明:C++把类型分成内置类型(基本类型)和⾃定义类型。
    • 内置类型就是语⾔提供的原生数据类型
    • 自定义类型就是我们使用类型class/struct等关键字自己定义的

4.2 构造函数

任务是在对象实例化时初始化对象,是特殊的成员函数,可自动调用。

构造函数的特点:

  • 函数名与类名相同。
  • 无返回值。 (返回值、void都不要,C++规定如此)
  • 对象实例化时系统会自动调用对应的构造函数。
  • 构造函数可以重载。
  • 类中无显式定义构造函数,C++编译器自动生成⼀个⽆参的默认构造函数
  • 不传实参就可以调⽤的构造就叫默认构造。
    • ⽆参构造函数、全缺省构造函数、我们不写构造时编译器默认⽣成的构造函数,都是默认构造函数(只能1个存在)。无参构造函数和全缺省构造函数虽然构成函数重载,但是调⽤时会存在歧义。
  • 内置类型成员变量的初始化不确定,看编译器。
  • 自定义类型成员变量,调⽤成员变量的默认构造函数初始化。(没有则报错,要初始化成员变量用初始化列表才可)

带图理解


4.3 析构函数

C++规定对象在销毁时会自动调用析构函数,完成对象中资源的清理释放工作,有些成员函数严格上可以不用

析构函数的特点:

  • 析构函数名:类前加上 ~
  • 无参数无返回值
  • ⼀个类⼀个析构函数。未显式定义则自动生成默认的析构函数。
  • 对象⽣命周期结束时,系统会自动调用析构函数。
  • 不显式定义,自动生成的析构函数对内置类型成员不做处理,自定类型成员则会调用。(实质上无论显不显式,自定义成员都会调用析构函数)
  • 有资源申请则需写,没有申请资源可不写,直接使⽤编译器⽣成的默认析构函数。默认⽣成的析构足够,也不需要显示写析构。
  • ⼀个局部域的多个对象,C++规定后定义的先析构。

带图理解


4.4 拷贝构造函数

第⼀个参数是自身类类型的引用,且任何额外的参数都有默认值

拷贝构造的特点:

  • 拷贝构造函数是构造函数的⼀个重载。
  • 第一个参数必须是类类型对象的引用(传值直接报错,无穷递归调用)
    可多个参数,除第一个外的参数必须有缺省值。
  • C++规定自定义类型对象进行拷贝行为必须调用拷贝构造,so自定义类型传值传参和传值返回都会调⽤拷贝构造完成。
  • 未显式自动生成,然后对内置类型成员变量会完成值拷贝/浅拷贝,
    对⾃定义类型成员变量会调⽤他的拷贝构造。
  • 类成员变量是内置类型且没有指向什么资源,自动生成的足用不用显式。
    是内置类型但指向了资源,自动的不够(值拷贝/浅拷贝),要深拷贝
    是⾃定义类型A成员,自动生成的拷贝构造会自己调用A的拷贝构造。
    显式实现了析构并释放资源要显⽰写拷贝构造,反之不用
  • 传值返回会产⽣⼀个临时对象调⽤拷贝构造,传值引用返回,返回的是返回对象的别名(引⽤),没有产⽣拷⻉。
    (返回对象是当前函数局部域的局部对象,函数结束就销毁,使⽤引⽤返回相当于⼀个野引⽤)
  • 传引用返回可以减少拷⻉,但要确保函数结束后返回对象还在。

带图理解


5. 赋值运算符重载


5.1 运算符重载

运算符被⽤于类类型的对象,通过运算符重载的形式指定新的含义

C++规定类对象使用运算符时,要++调用对应运算符重载(没有则报错)++

  • 运算符重载名:operator+要定义的运算符。具有其返回类型数列表及函数体和参
  • ⼆元运算符的左侧运算对象传给第⼀个参数,右侧运算对象传给第⼆个参数
    重载函数是成员函数,则第⼀个运算对象默认传给隐式的this指针
  • 不能通过连接语法中没有的符号来创建新的操作符:⽐如operator@
  • .* :: sizeof ? : . 上5个运算符不能重载
  • C++规定,后置++重载时,增加⼀个int形参,跟前置++构成函数重载
  • 重载<<和>>时,需要重载为全局函数(成员函数时this指针占第⼀个形参位置)
  • 重载操作符⾄少有⼀个类类型参数,不能通过运重载改变内置类型对象含义
  • 需要重载哪些运算符,要看哪些运算符重载后有意义算符

带图理解


5.2 赋值运算符重载

默认成员函数,⽤于完成两个已经存在的对象直接的拷贝赋值。

  • 必须重载为成员函数。参数建议写成const 当前类类型引⽤(值传参有拷贝)
  • 有返回值(建议写成当前类类型引⽤,提⾼效率)可以⽀持连续赋值场景
  • 没有显式⾃动⽣成默认赋值运算符重载,
    对内置类型成员变量是值拷⻉/浅拷⻉,
    对**⾃定义类型成员变量**是调⽤赋值重载函数。
  • 类成员变量全是内置类型+没有指向什么资源,⾃动⽣成的足够。
    内置类型+指向了资源,要实现深拷⻉。
  • 内部要是⾃定义类型A成员,该重载会调⽤A的赋值运算符重载。
  • 显⽰实现了析构并释放资源,就要显⽰写赋值运算符重载,反之不用

6. 取地址运算符重载


6.1 const成员函数

const修饰的成员函数

  • const修饰成员函数放到成员函数参数列表的后⾯
  • const实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进⾏修改

6.2 取地址运算符重载

分为普通取地址符重载和const取地址运算符重载

一般⾃动⽣成的够⽤


7. 再探构造函数


7.1 初始化列表

初始化成员变量可用初始化列表,

  • 冒号开始,接⼀个以 ,分隔的数据成员列表,后跟(初始值/表达式)
  • 每个成员变量在初始化列表中只能出现⼀次,语法理解上++初始化列表可说是每个成员变量定义初始化的地⽅++
  • 引用成员变量const成员变量没有默认构造的类类型变量,必须放在初始化列表位置进⾏初始化,否则会编译报错
  • C++11⽀持在成员变量声明的位置给缺省值,该值是给没有显示在初始化列表初始化的成员使⽤的。
  • 尽量使用初始化列表初始化,不在初始化列表初始化的成员也会⾛ 初始化列表。

7.2 初始化情况(缺省值)

声明位有缺省值,初始化列表会用这个缺省值初始化。反之没有值,

  • 没显示在初始化列表初始化的内置类型成员是否初始化取决编译器。
    没显示在初始化列表初始化的自定义类型成员调⽤该类型的默认构造函数,没有则报错。
  • 初始化列表中按成员变量++在类中声明顺序进行初始化++ ,跟成员在初始化列表出现的的先后顺序⽆关。最好声明顺序和初始化列表顺序⼀致

结合图理解


8. 类型转换


8.1 隐式类型转换

C++⽀持++内置类型隐式类型++ 转换为类类型对象,需要有相关内置类型为参数的构造函数。


8.2 explicit

构造函数前⾯加explicit就不再⽀持隐式类型转换


8.3 类与类

类类型的对象之间也可以隐式转换,需要相应的构造函数支持


9. static成员


9.1 静态成员变量

⽤static修饰的成员变量,一定在类外进⾏初始化

  • 静态成员变量为所有类对象所共享,存放在静态区,不属于某个具体的对象,不存在对象中

9.2 静态成员函数

⽤static修饰的成员函数,静态成员函数没有this指针

  • 静态成员函数中可访问其他静态成员,但不能访问⾮静态的(无this指针)
  • ⾮静态的成员函数,可以访问任意的静态成员变量和静态成员函数。

9.3 访问情况

  • 突破类域可以访问静态成员,通过类名::静态成员对象.静态成员 来访问静态成员变量和静态成员函数。
  • 静态成员也是类的成员,受public、protected、private 访问限定符的限制
  • 静态成员变量不能在声明位置给缺省值初始化,因为缺省值是个构造函数初始化列表的,静态成员变量不属于某个对象,不⾛构造函数初始化列表

带图理解


10. 友元


10.1 友元概念

友元是⼀种突破类访问限定符封装的⽅式,分为:友元函数+友元类,

  • 在函数/类声明的前⾯加friend,并且把友元声明放到⼀个类的⾥⾯
    (友元函数声明可在类任意地⽅,不受类访问限定符限制)(增加耦合度,破坏封装)

10.2 访问及关系

  • 外部友元函数可访问类的私有和保护成员,友元函数只是声明,他不是类的成员函数
  • ⼀个函数可以是多个类的友元函数。
  • 友元类中的成员函数都可以是另⼀个类的友元函数,都可以访问另⼀个类中的私有和保护成员
    但是友元类的关系是单向的,不具有交换性,也不能传递


11. 内部类


11.1 内部类概念

内部类: ⼀个类定义在另⼀个类的内部。

  • 是独⽴的类,受外部类类域限制和访问限定符限制,故外部类定义的对象中不包含内部类。
  • 内部类默认是外部类的友元类
  • 内部类也是⼀种封装,当A类跟B类紧密关联,实现A类就是给B类⽤,那A可设为B的内部类
    如果放到private/protected位置,那么A类就是B类的专属内部类,其他地方不行

带图理解


完~走过路过不要错过

相关推荐
JavaPub-rodert1 小时前
我把 OpenAI 协议塞进了 Go 工具库:go-commons 开始支持 AI 了
开发语言·人工智能·golang
caimouse1 小时前
ReactOS 图形系统分析(23):引擎内存管理 — mem.c
c语言·开发语言
caimouse1 小时前
ReactOS 图形系统分析(15):窗口对象 — EWNDOBJ(engwindow.c)
c语言·开发语言
青 春 记 忆1 小时前
零基础入门python07:让程序记住数据——JSON文件和异常处理
开发语言·windows·python·json·python3.11
举手1 小时前
Epoll模型
linux·c++·学习
W_326001 小时前
Python 常用标准 / 第三方库:random、tqdm、turtle、jieba 用法
开发语言·python
Shadow(⊙o⊙)1 小时前
Linux网络部分——TCP服务端、客户端架构分析多进程、多线程、线程池
c++·tcp/ip·架构
zh路西法2 小时前
【3D SLAM源码解读系列】(四) GTSAM与iSAM2——从回环约束到位姿图优化
c++·slam·gtsam·pgo·isam2
caimouse2 小时前
ReactOS 图形系统分析(17):区域填充 — EngPaint / IntEngPaint(paint.c)
c语言·开发语言·算法