【C++】C++入门 — 类和对象初步介绍

类和对象

1 类的作用域

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

cpp 复制代码
class person{
	public:
	void personage();
	pubilc:
	char* _name;
	int _age;
	int _class;	
};
//需要标明作用域 才能正确定义
void person::personage(){
	cout << _name << endl;
}

2 类的实例化

类的实例化就是创建类对象

  1. 类是对对象进行描述的,是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它
    比如:入学时填写的学生信息表,表格就可以看成是一个类,来描述具体学生信息。
    只有我填了表并上交保存才是创建了一个新对象。完成了实例化。
  2. 一个类可以实例化出多个对象 ,实例化出的对象占用实际的物理空间,存储类成员变量
    Person类是没有空间的,只有Person类实例化出的对象才有具体的年龄

以上述为例:

cpp 复制代码
int main(){
	//进行实例化
	preson jack;
	
	jack._name = "Jack";
	jack._age = 18;
	jack._class = 01;
	
	return 0;
}

3 类对象模型

问题:类中既可以有成员变量,又可以有成员函数

那么一个类的对象中包含了什么? 如何计算一个类的大小?

我们来做一下实验:对刚才的 person类 进行大小计算:

可见person类的大小是 16(X64环境)

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

类的成员函数不占用对象内存区,函数的分布位置为代码区,同一个类实例化的所有对象共享相同的函数。

这是如何计算出来的呢?

其实与结构体类似,具有相同的对齐规则,

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

面试题:
结构体怎么对齐? 为什么要进行内存对齐?

在C++中,类(class)的成员变量在内存中的布局需要遵循内存对齐规则,主要是出于以下几个关键原因:

  1. 性能优化:(主要原因)

    • 访问未对齐的内存地址在某些硬件架构上可能导致性能下降。例如,许多处理器在访问自然边界(通常是2、4、8字节的倍数地址)上的数据时效率最高。如果一个4字节的数据没有按照4字节对齐,处理器可能需要执行两次内存访问操作来获取完整的数据,这无疑会降低程序运行速度。
  2. 硬件要求

    • 一些硬件平台(如ARM、x86等)的指令集直接要求对某些类型的数据进行对齐访问,否则会导致数据错误或触发硬件异常。例如,SSE指令在处理向量数据时就需要16字节对齐。
  3. 缓存效率

    • 内存对齐也有助于提高缓存的使用效率。现代CPU使用多级缓存系统,通常以固定大小的块(缓存行)从主内存加载数据。如果对象在内存中被正确对齐,那么该对象可能会更高效地填充缓存行,从而减少缓存冲突和伪共享现象。
  4. 平台移植性

    • 不同的计算机体系结构可能有不同的内存对齐要求,通过在编程语言层面实现内存对齐,可以保证代码在不同平台上具有更好的可移植性和一致性。

因此,在C++中编译器默认会对类的成员变量进行内存对齐,当然也可以通过预定义的编译器宏(如#pragma pack)或者显式指定成员变量的对齐方式来控制类的内存布局。

4 this指针

介绍:

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

cpp 复制代码
#include<iostream>
using namespace std;
class Date {
public:
	Date(int year = 2024, int month = 1, int day = 1) {
		_year = year;
		_month = month;
		_day = day;
	}

	void show() {
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main() {

	Date today(2024, 2, 4);
	Date yesterday(2024, 2, 3);
	
	today.show();

	return 0;
}

对于上述类,有这样的一个问题:

Date类中有 Init 与 Print 两个成员函数,函数体中没有关于不同对象的区分,那当d1调用默认构造函数时,该函数是如何知道应该设置today对象,而不是设置yesterday对象呢?

C++中通过引入this指针解决该问题

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

特性:

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

【面试题】

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

this指针存在于哪里?

在C++中,this指针是一个隐含的、非静态成员函数内部可用的特殊指针,它指向当前正在调用该成员函数的对象实例。
每次调用非静态成员函数时,编译器都会自动将对象的地址作为额外的第一个参数传递给该函数

尽管在源代码中我们并不直接看到这个参数。在函数体内部,this关键字用于引用这个隐含的指针。

因此,this指针实际上是存在于每个非静态成员函数的执行上下文中,并且它始终指向当前调用该函数的对象实例。

this指针可以为空吗?

在常规情况下,当一个有效的对象调用其成员函数时,this指针不应该为空。然而,在某些特定情况下,this指针确实可能为空,特别是在不正确的使用情况下,比如:

  • 当对象尚未完全构造完成时,即在构造函数初始化列表结束前或进入构造函数主体之前访问this,这时的行为是未定义的,编译器不会阻止这样的行为,但可能导致崩溃或其他不可预测的结果。
  • 如果通过一个空指针调用了成员函数,如同其他间接调用一样,这是典型的运行时错误,表现为"空指针异常"(Null Pointer Exception)。例如:
cpp 复制代码
    MyClass* obj = nullptr;
    obj->someFunction(); // 这将引发空指针异常,因为试图通过nullptr调用成员函数
  • 在C++11引入右值引用之后,移动构造函数或移动赋值运算符中,当源对象即将被移动(资源转移)后置为无效状态时,也可能出现类似情况,但这不是this本身为空,而是对象即将变成无效状态。

总之,正常情况下,程序员应该避免让this指针处于空状态,确保在成员函数调用期间对象的有效性。如果需要检查this是否为空,可以在成员函数开始时添加适当的断言来防止潜在的问题。

Thanks♪(・ω・)ノ谢谢阅读!

下一篇文章见!!!

相关推荐
DKPT22 分钟前
Java桥接模式实现方式与测试方法
java·笔记·学习·设计模式·桥接模式
好奇的菜鸟2 小时前
如何在IntelliJ IDEA中设置数据库连接全局共享
java·数据库·intellij-idea
tan180°2 小时前
MySQL表的操作(3)
linux·数据库·c++·vscode·后端·mysql
学不动CV了2 小时前
ARM单片机启动流程(二)(详细解析)
c语言·arm开发·stm32·单片机·51单片机
DuelCode3 小时前
Windows VMWare Centos Docker部署Springboot 应用实现文件上传返回文件http链接
java·spring boot·mysql·nginx·docker·centos·mybatis
优创学社23 小时前
基于springboot的社区生鲜团购系统
java·spring boot·后端
好好研究3 小时前
学习栈和队列的插入和删除操作
数据结构·学习
幽络源小助理3 小时前
SpringBoot基于Mysql的商业辅助决策系统设计与实现
java·vue.js·spring boot·后端·mysql·spring
猴哥源码3 小时前
基于Java+springboot 的车险理赔信息管理系统
java·spring boot
彭祥.3 小时前
Jetson边缘计算主板:Ubuntu 环境配置 CUDA 与 cudNN 推理环境 + OpenCV 与 C++ 进行目标分类
c++·opencv·分类