C++学习__02—类与对象(上篇)

本文介绍了C++中的面向对象编程概念,包括类的初步认识、类的定义、访问限定符(封装)、类的作用域、实例化过程以及this指针的使用。通过外卖系统的比喻,解释了面向过程与面向对象的区别,强调了类封装的重要性。
摘要由CSDN通过智能技术生成

1.面向过程和面向对象初步认识
2.类的引入
3.类的定义
4.类的访问限定符及封装
5.类的作用域
6.类的实例化
7.类对象模型
8.this指针

(建议通过计算机设备进行观看,以获得更佳的视觉效果。若选择在移动设备上观看,可能会因屏幕限制导致部分内容的排版效果不理想,且信息展示过于密集,从而影响观看体验。)

(  本内容有大量借鉴比特课件,故特此说明!)

(书写之时,有赶时之意,故必有不完善粗糙之处!!!)

1.面向过程和面向对象初步认识

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

(可能有些迷,就像是一个外卖系统一样,C语言要关注怎么下单,外卖小哥如何获取信息等,

但在C++中,只有三个对象,用户,外卖小哥,商家,就这三个对象来编写代码,如用户,需要有 姓名,地址,电话...,他与商家进行交互,即商家获取用户需求...,既而商家与外卖小哥进行交互...,外卖小哥这个对象又需要实时定位,确定所需时间...) (可能我说的会更迷,鉴定观看)

2.类的引入

C语言中,结构体中只能定义变量,在C++中,结构体内不仅可以定义变量,也可以定义函数

上面结构体的定义,在C++中更喜欢用class来代替

(可能会有疑问,上面这个结构体,_name, _gender, _age 不是在后面来的吗?前面的函数怎么用的呢?这个得要到讲到 this 指针 时才能说明)

3.类的定义

class 为定义类的关键字className 为类的名字{ } 中为类的主体注意类定义结束时后面分号

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

类的两种定义方式

1.声明和定义全部放在类体中,需要注意:成员函数如果在类中定义,编译器可能会将其当成内联函数处理。(声明和定义的区别? 声明是一种承诺,承诺要干嘛,但是还没做,定义就是把这个事落地了)

2.声明放在.h文件中,类的定义放在.cpp文件中

一般情况下,更期望采用第二种方式。(效率更高)

(关于public 往下看)

4.类的访问限定符及封装

4.1访问限定符


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

[ 访问限定符说明 ]
1. public 修饰的成员在类外可以直接被访问
2. protected 和 private 修饰的成员在类外不能直接被访问 ( 此处 protected 和 private 是类似的 )
3. 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
4. class 的默认访问权限为 private ,struct 为public (因为 struct 要兼容 C )
注意: 访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别
 


问题: C++ 中 struct 和 class 的区别是什么?
解答: C++需要兼容C语言,所以C++中 struct 可以当成结构体去使用。另外 C++ 中 struct 还可以用来定义类.和 class 是定义类是一样的,区别是 struct 的成员默认访问方式是 public ,class 的成员默认访问方式是 private.(即什么限定符都不用的话,采用默认访问方式)

4.2 封装

!!! 面向对象的三大特性: 封装、继承、多态 !!!
在类和对象阶段,我们只研究类的封装特性,那什么是封装呢?


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


封装本质上是一种管理 :  我们如何管理兵马俑呢 ? 比如如果什么都不管,兵马俑就被随意破坏了。那么我们首先建了一座房子把兵马俑给封装起来。但是我们目的全封装起来,不让别人看。所以我们开放了售票通道,可以买票突破封装在合理的监管机制下进去参观。类也是一样,我们使用类数据和方法都封装到一下。不想给别人看到的,我们使用 protected / private 把成员封装起来开放一些共有的成员函数对成员合理的访问。所以封装本质是一种管理。

5.类的作用域

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

(这里建议命名基本类型时 (int,char之类的),都在前面加上” _ ")

6.类的实例化

用类类型创建对象的过程,称为类的实例化
1. 类只是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它。
2. 一个类可以实例化出多个对象,实例化出的对象 占用实际的物理空间,存储类成员变量
3. 做个比方。类实例化出对象就像现实中使用建筑设计图建造出房子,类就像是设计图,只设计出需要什么东西,但是并没有实体的建筑存在,同样类也只是一个设计,实例化出的对象才能实际存储数据,占用物理空间。

7.类对象模型

7.1如何计算类对象的大小

问题: 类中既可以有成员变量,又可以有成员函数,那么一个类的对象中包含了什么?如何计算一个类的大小?

7.2类对象的存储方式猜测

a.对象中包含类的各个成员

缺陷:每个对象中成员变量是不同的,但是调用同一份函数,如果按照此种方式存储,当一个类创建多个对象时,每个对象中都会保存一份代码,相同代码保存多次,浪费空间。那么如何解决呢?

b.只保存成员变量,成员函数存放在公共的代码段

问题:对于上述两种存储方式,那计算机到底是按照那种方式来存储的?
我们再通过对下面的不同对象分别获取大小来分析看下

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

7.3结构体内存对齐规则


1. 第一个成员在与结构体偏移量为0的地址处。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。

注意:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值
VS中默认的对齐数为8
3.结构体总大小为: 最大对齐数 (所有变量类型最大者与默认对齐参数取最小)的整数倍.
4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数 (含嵌套结构体的对齐数)的整数倍。


问题:(答案可略看)
1.结构体怎么对产? 为什么要进行内存对齐
2.如何让结构体按照指定的对齐参数进行对齐
3.什么是大小端? 如何测试某台机器是大端还是小端,有没有遇到过要考虑大小端的场景

(1.

内存对齐是指数据在内存中的起始地址按照某个规则进行排列,这个规则通常是数据类型大小的倍数。例如,一个int类型的变量在32位系统上通常是4字节对齐的,这意味着它的起始地址应该是4的倍数。

进行内存对齐的原因主要有以下几点:

  1. 硬件效率:

    • 许多处理器访问未对齐的内存地址时需要额外的操作,这会降低内存访问速度。对齐可以确保数据按照处理器的最有效方式进行访问。
  2. 数据完整性:

    • 某些硬件平台在读取或写入内存时,如果遇到未对齐的地址,可能会导致总线错误或异常。
  3. 性能优化:

    • 对齐可以减少内存访问次数,提高缓存利用率,从而提升程序运行效率。
  4. 跨平台兼容性:

    • 不同的硬件架构可能对内存对齐有不同的要求,遵循一定的对齐规则可以提高代码的可移植性。

2.

在C++中,可以使用alignas关键字来指定结构体的对齐参数。alignas是C++11标准引入的,用于指定类型或变量的对齐要求。

以下是使用alignas关键字来指定结构体对齐的示例:

#include <iostream>

// 定义一个结构体,并指定对齐参数为16字节
struct alignas(16) MyStruct {
    char a;
    int b;
    short c;
};

int main() {
    // 创建结构体的实例
    MyStruct myInstance;

    // 输出结构体大小和地址
    std::cout << "Size of MyStruct: " << sizeof(MyStruct) << " bytes" << std::endl;
    std::cout << "Alignment of MyStruct: " << _Alignof(MyStruct) << " bytes" << std::endl;
    std::cout << "Address of myInstance: " << &myInstance << " (Expected alignment: 16)" << std::endl;

    return 0;
}

在这个例子中,MyStruct结构体被指定为按照16字节对齐。这意味着结构体的每个实例的起始地址都将是16的倍数,结构体的大小也会根据对齐要求进行调整。

alignas关键字不仅可以用于结构体和类,还可以用于变量和函数。它是指定对齐要求的首选方法,因为它是标准C++的一部分,并且比预处理器指令(如#pragma pack)更加标准和可移植。

需要注意的是,对齐参数应该选择合理的值。过大的对齐要求可能会导致不必要的内存浪费,而过小的对齐要求可能无法满足硬件的要求,从而影响性能。通常,对齐参数应该基于目标平台的要求和最大数据类型的大小来选择。

3.

大小端(Endianness)是指在计算机系统中表示多字节数据类型(如整数、浮点数等)时,字节的存储顺序。这种顺序对于数据的传输和处理非常重要,因为不同的计算机架构可能采用不同的存储方式。

### 大端(Big-Endian):
在大端存储模式中,多字节数据的最高有效字节(MSB)存储在最低的内存地址,其余字节按顺序存储在更高的地址。例如,一个32位的整数`0x12345678`在大端存储中将按照`12 34 56 78`的顺序存储。

### 小端(Little-Endian):
在小端存储模式中,最低有效字节(LSB)存储在最低的内存地址,其余字节按相反的顺序存储在更高的地址。同样的整数`0x12345678`在小端存储中将按照`78 56 34 12`的顺序存储。

### 测试机器的大小端:
在C或C++中,可以通过编写一个简单的程序来测试机器的大小端。以下是一个C++示例:

```cpp
#include <iostream>

int main() {
    unsigned int x = 0x12345678;
    char *c = (char*)&x;

    // 如果第一个字节是x的最高有效字节,则是小端
    if (*c == 0x78) {
        std::cout << "This machine is Little-Endian." << std::endl;
    } else {
        std::cout << "This machine is Big-Endian." << std::endl;
    }

    return 0;
}
```

在这个程序中,我们创建了一个`unsigned int`类型的变量`x`,并给它赋值为`0x12345678`。然后我们将`x`的地址转换为`char*`类型,并检查第一个字节的值。如果第一个字节是`0x78`(即`0x12345678`的最低有效字节),则机器是小端;否则,机器是大端。

### 考虑大小端的场景:
大小端问题主要出现在跨平台数据交换时,尤其是网络通信和文件存储。例如:

1. **网络协议**:
   在网络通信中,为了确保数据在不同架构的计算机之间正确传输,通常需要使用网络字节顺序(大端)来统一数据格式。

2. **文件格式**:
   在设计文件格式或数据序列化时,需要考虑大小端问题,以确保数据在不同系统间读写时的一致性。

3. **硬件接口**:
   与硬件设备交互时,如嵌入式系统开发,可能需要根据硬件的存储顺序来处理数据。

4. **跨平台库和API**:
   开发跨平台的库或API时,需要处理不同系统间的大小端差异,以确保兼容性。

在这些场景中,开发者通常需要使用特定的函数或方法来处理大小端问题,例如在C++中可以使用`htonl`、`htons`、`ntohl`、`ntohs`等函数来在网络字节顺序和主机字节顺序之间转换。在C中,可以使用`hton`系列的宏或函数来进行转换。此外,一些编程语言提供了内置的方法来处理大小端问题。

)

8.this 指针

8.1 this 指针的引出


我们先来定义一个日期类Date

对于上述类,有这样的一个问题:
Date类中有SetDate与Display两个成员函数,函数体中没有关于不同对象的区分,那当s1调用SetDate函数时,该函数是如何知道应该设置s1对象,而不是设置s2对象呢?


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

(若是不隐藏写,则为此)

8.2 this的特性

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

问题:

1. this指针存在哪里?
2. this指针可以为空吗?

1.不能

2.会崩溃,在 PrintA 这个函数这里,因为有 this->_a 这个操作

但这个函数不会

  • 37
    点赞
  • 59
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值