ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

C++: 类与对象(上)

C++: 类与对象(上)

1. 类的定义

1.1 类定义格式

在 C++ 中,类是一种用户自定义的数据类型,它将数据成员和成员函数封装在一起。类定义的基本格式如下:

class ClassName { // 成员变量 // 成员函数 }; // 注意末尾的分号

定义在类里面的函数默认都为内联(inline)函数。

例如,定义一个简单的日期类:

class Date { public: void Init(int year, int month, int day) { _year = year; _month = month; _day = day; } void Print() { cout << _year << "-" << _month << "-" << _day << endl; } private: int _year; int _month; int _day; };

1.2 访问限定符

C++ 提供了三种访问限定符,用于控制类成员的访问权限:

  • public:公有成员,在类外可以直接访问。
  • protected:保护成员,在类外不能直接访问,但可以在派生类中访问。
  • private:私有成员,在类外不能直接访问,只能在类内部访问。

访问限定符的作用域从该限定符出现的位置开始,到下一个访问限定符出现之前结束。如果类(class)定义开头没有写访问限定符,则默认是 private。

1.3 类域

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

// 在类外定义成员函数 void Date::Print() { cout << _year << "-" << _month << "-" << _day << endl; }

2. struct 来表示类

在 C++ 中,struct也可以当作类定义的关键字来使用。用struct定义的类与用class定义的类在功能上几乎完全一致,都可以包含数据成员和成员函数,也都可以使用访问限定符。

两者的唯一区别在于默认的访问限定符不同:

  • struct的默认访问限定符是public,即成员在类外可以直接访问。
  • class的默认访问限定符是private,即成员在类外不能直接访问。

例如,下面用struct定义一个日期类:

struct Date { // 默认是 public,可以直接访问 void Init(int year, int month, int day) { _year = year; _month = month; _day = day; } int _year; int _month; int _day; };

因此,在 C++ 中struct和class都可以用来定义类,选择哪一个主要取决于默认访问权限的偏好:希望成员默认公开时用struct,希望成员默认私有时用class。

注意:struct 在 C++ 中可以直接作为类型来定义变量,当然C中的做法同样兼容。

typedef struct ListNode { int val; ListNode* next;//struct ListNode* next; };

2. 实例化

2.1 实例化概念

用类类型创建对象的过程,称为类的实例化。类本身只是一个模板,不占用实际的存储空间;只有实例化出对象后,系统才会为对象分配内存。

在类中的成员变量只是声明,并未开空间。

Date d1; // 实例化一个 Date 对象 Date d2; // 实例化另一个 Date 对象

同一个类可以实例化出多个对象,每个对象都拥有自己独立的数据成员存储空间,但成员函数是共享的。

2.2 对象大小

对象的大小等于其所有非静态数据成员的大小之和,同时需要考虑内存对齐规则。成员函数不占用对象空间,因为它们存放在代码段中,由所有对象共享。

补充:内存对齐的优势?

  1. 提高 CPU 访问内存的效率,避免一次变量跨两次内存块读取;
  2. 适配部分硬件,非对齐访问会直接报错崩溃;
  3. 有利于 CPU 缓存,提升缓存命中率;
  4. 便于 DMA、硬件寄存器等底层硬件操作。

内存对齐不是没有代价:会产生内存空洞(padding 填充字节),浪费少量内存空间,属于空间换时间。

class A { public: void Func() {} // 成员函数不占对象空间 private: int _a; // 4 字节 char _b; // 1 字节,考虑对齐后可能占 4 字节 }; // sizeof(A) 在 32 位平台下通常为 8 字节

空类的大小为 1 字节,这是为了区分不同的空对象地址。

2.3 内存对齐规则

内存对齐是指编译器在分配结构体或类对象的内存时,按照一定的规则在成员之间插入填充字节(padding),使每个成员的起始地址满足对齐要求。C++ 中主要遵循以下几条规则:

  • 第一个成员变量,放在结构体 / 类偏移为0的地址处。
  • 其余成员变量:存放起始地址必须是对齐数的整数倍。
    • ✅对齐数计算公式:对齐数 = min(编译器默认对齐数,该成员自身大小)
    • VS 默认对齐数:8;
  • 结构体 / 类整体总大小:必须是最大对齐数的整数倍。
  • 最大对齐数:所有成员各自对齐数里面的最大值。
  • 嵌套结构体场景
    • 被嵌套的结构体:对齐到它自己的最大对齐数的整数倍地址。
    • 外层结构体总大小:必须包含所有成员(嵌套结构体也算),整体是全部最大对齐数的整数倍。

下面通过一个例子来理解对齐过程:

struct S { char _c; // 1 字节,偏移 0 int _i; // 4 字节,需对齐到 4 的倍数,偏移 4 char _d; // 1 字节,偏移 8 }; // 总大小 = 12 字节(最大对齐数 4,12 是 4 的倍数) struct A { char a; // 1字节 int b; // 4字节 }; struct B { char x; // 1字节 struct A sa; //按照A最大对齐数4,大小为8 double y; // 8字节 }; //总大小是:24

3. this 指针

C++ 编译器给每个非静态成员函数增加了一个隐藏的指针参数,称为 this 指针。this 指针指向调用该成员函数的对象,通过它可以在成员函数内部访问对象的成员。

void Date::Print() { // this 指向调用 Print 的对象 cout << this->_year << "-" << this->_month << "-" << this->_day << endl; }

this 指针的特点:

  • this 指针的类型是Class Name* const,即指针本身不能被修改。
  • this 指针是成员函数的隐含参数,不需要也不能在调用时显式传递。
  • this 指针存储在栈上,是成员函数的一个局部变量。
  • C++规定不能在实参和形参的位置显示的写this指针(编译时编译器会处理),但是可以在函数体内显示使用this指针。
  • this 指针存在于内存中的栈。

4. C++ 和 C 语言实现 Stack 对比

下面分别用 C 语言和 C++ 实现一个栈,对比两种语言在数据封装和代码组织上的差异。

C 语言实现

typedef struct Stack { int* _a; int _top; int _capacity; } Stack; void StackInit(Stack* ps) { ps->_a = (int*)malloc(sizeof(int) * 4); ps->_top = 0; ps->_capacity = 4; } void StackPush(Stack* ps, int x) { if (ps->_top == ps->_capacity) { ps->_capacity *= 2; ps->_a = (int*)realloc(ps->_a, sizeof(int) * ps->_capacity); } ps->_a[ps->_top++] = x; }

C 语言中,数据和操作数据的函数是分离的,函数必须显式接收结构体指针作为参数,并且结构体成员默认是公开的,缺乏访问控制。

C++ 实现

class Stack { public: void Init() { _a = new int[4]; //new 是C++开辟空间的一个关键字,后续介绍 _top = 0; _capacity = 4; } void Push(int x) { if (_top == _capacity) { _capacity *= 2; int* tmp = new int[_capacity]; for (int i = 0; i < _top; ++i) tmp[i] = _a[i]; delete[] _a; _a = tmp; } _a[_top++] = x; } private: int* _a; int _top; int _capacity; };

C++ 将数据和操作封装在类中,成员函数通过 this 指针隐式访问对象数据,代码更简洁;同时通过 private 访问限定符保护数据,增强了安全性。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进