C++类和对象(上篇)

Posted Suk-god

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++类和对象(上篇)相关的知识,希望对你有一定的参考价值。

文章目录

面向过程 && 面向对象

面向过程,关注的是过程,分析求解问题的步骤,通过函数调用逐步解决问题
C++是基于面向对象的。关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成

类 && 对象

概念区分

:用来对实体(对象)进行描述,对象有什么属性以及对象具有什么功能。是一种自定义类型
对象:现实生活中实实在在存在的,对象是类的具体表现。用类类型创建出来的变量—>对象

类的定义

使用struct和class都可以定义类,在C++中一般使用class定义类

格式:class classname.........;

class:定义类的关键字
classname:类的名称
…为类的主体,以;结尾

类中的元素称为类的成员
类中的数据称为类的属性或者成员变量
类中的函数称为类的方法或者成员函数

  • 类的两种定义方式
    1、声明和定义都放在类体中
    注意:成员函数如果在类中定义,编译器可能会将其当做内联函数处理

    2、声明和定义分开


    一般情况下,推荐使用方式2

类的访问限定符及封装

访问限定符

限定符种类

  1. public(公有)
    修饰的成员在类外可以直接被访问
  2. protected(保护)
    修饰的成员在类外不能直接被访问
  3. private(私有的)
    修饰的成员在类外不能直接被访问

目前看来,protected与private没有什么区别(实质上并不是,后续学到后再补充)

限定符作用域

访问权限的作用域从该访问限定符出现的地方开始直到下一个访问限定符出现为止

几点补充

  1. class的默认访问权限为private,struct的默认访问权限为public(因为要兼容C)
  2. 访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别

面试题:C++中classstruct的区别是什么

解答:

  1. C++需要兼容C语言,所以C++中的struct可以当做结构体去使用。
  2. C++中的struct还可以定义类,和class定义类是一样的。区别是struct的类成员默认访问方式是public,class的类成员默认访问方式private

封装

什么是封装?

将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互

C++如何实现封装的?

用类将对象的属性和方法结合在一块,让对象更加完善。通过访问权限选择性的将其接口提供给外部的用户使用

面向对象的三大特征

封装 继承 多态

有时候会说四大特征:在原有的基础上增加一个抽象

抽象:对一个复杂事物的认知过程

类的作用域

C++中的作用域

  1. 全局作用域
  2. 函数体内部的局部作用域
  3. 命名空间
  4. 类域

类域

类定义了一个新的作用域,类的所有成员都在类的作用域中。在类体外定义成员,需要使用 :: 作用域解析符指明成员属于哪个类域。

类的实例化

用类类型创建对象的过程称为类的实例化

类只是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它

一个类可以实例化出多个对象,实例化出的对象 占用实际的物理空间,存储类成员变量

类对象模型

计算类对象的大小

分析:类实例化出来的对象中不仅有成员变量,还有成员函数

因此我们就有以下几种设想:

  1. 计算一个类的大小是类中成员变量的大小 + 成员函数的大小

  2. 成员函数单独存储一份,不同的变量调用同一个成员函数,因此只需要计算成员变量的大小 + 一个指针的大小(该指针指向单独存储的成员函数)

接下来我们实际验证一波儿


事实告诉我们我们上面的两种设想都是错误的!!!
结论
一个类的大小,实际就是该类中成员变量之和,当然也要进行内存对齐!!!

空类的大小是多少呢?
测试一把
在VS013环境下:

关于空类大小为1的原因解释见下图:

在Linux环境下

它的结果也是1

类对象的存储方式

事实证明,在计算类对象大小的时候,不需要考虑成员函数,因为成员函数是被存储在公共代码区的

一个类的大小,实际就是该类中”成员变量”之和,当然也要进行内存对齐,注意空类的大小,空类比较特殊,编译器给了空类一个字节来唯一标识这个类。

几道面试题

  1. 结构体如何对齐,为什么要进行内存对齐??
    如何对齐,主要有4步骤
    ①第一个成员在与结构体偏移量为0的地址处
    ②其他成员变量要对齐到某个数(对齐数)的整数倍的地址处
    对齐数指的是min(编译器默认对齐数,成员变量大小)其中VS的默认对齐数是8
    ③结构体总的大小要满足最大对齐数的整数倍
    ④如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍。结构体整体的大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍
    为什么要进行内存对齐,原因有两点
    第一:平台原因
    不是所有的硬件平台都能访问任意地址上的任意数据的,某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常
    第二:性能原因
    数据结构(尤其是栈)应该尽可能的在自然边界上对齐。原因在于,为了访问未对齐的内存。处理器需要做两次内存访问。而对齐后的内存访问只需要一次访问。
    总的来说,结构体内存对齐是拿空间换时间的做法
  2. 如何让结构体按照指定的对齐参数进行对齐
    使用预处理命令

设置默认对齐数

 #pragma pack(xxx) //xxx:想要设置的默认对齐数

恢复默认对齐数

#pragma pack()

结构体在对齐方式不合适的时候,我们可以自己更改默认对齐数

  1. 什么是大小端?如何测试某台机器是大端还是小端。
    大小端:说的是数据在内存当中存储的两种不同方式

    测试大小端的3种方式:
    ①对一个整型变量赋初值为1,然后采用char*的指针去访问这个整数

    我的机器为小端

    ②采用联合结构union

    小端,验证完毕
    ③通过在VS中查看内存布局确定

  2. 空类有多大,为什么?

this指针

引入


问题:在Init() 和 Print()成员函数中,没有任何关于对象的说明,那这些成员方法在执行的时候,是如何知道要对哪个对象操作的?

这就是this指针的作用!!

即:C++中通过引入this指针解决该问题,即C++编译器给每个“非静态的成员函数”增加了一个隐藏的指针参数,让该指针指向当前对象(程序运行时调用该函数的对象),在函数体中所有成员变量的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成

this的特性

  1. this指针的类型:类类型* const
  2. 只能在“成员函数”的内部使用
  3. this指针本质上其实是一个成员函数的形参,是对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针。
  4. this指针是成员函数第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递

面试题

  1. this指针存在哪里?
    解答:this指针存放在栈上
    验证:
    思路:定义this的引用类型变量,打印this的地址与esp(栈顶)ebp(栈底)地址比较,如果在两者之间,就说明是在栈上定义的

  2. this指针可以为空吗?
    解答:可以!
    但是当this指针为空后,在调用成员函数的时候可能会崩溃。


    上面是对类的定义
    紧接着看下面的代码片段

    首先,它是可以通过编译的,指针P的值会由编译器传递给this指针,那么也就是说,this指针是可以为空的。
    紧接着我们向下运行代码:

    紧接着进入Init函数的内部

    我们发现程序崩溃了。

总结一下:
this指针可以为空,当this指针为空的时候,如果在成员函数中没有访问任何成员变量或者成员函数,则代码不会崩溃,否则就会崩溃。

OK~~
以上就是本次的全部内容!各位看官请留下你们的足迹~~

以上是关于C++类和对象(上篇)的主要内容,如果未能解决你的问题,请参考以下文章

C++从青铜到王者第二篇:C++类和对象(上篇)

C++初阶:类和对象(上篇)类的定义 | 类的访问限定符及封装 | 类的作用域 | 类的实例化 | 类对象模型 | this指针

成功创建c ++类和对象[重复]

C++类和对象--对象特性

C++类和对象中

C++类和对象--运算符重载