C++类和对象(上):面向对象编程的基石
目录
前言:
一、类的定义:封装的起点
1.1 类定义格式
1.2 访问限定符
1.3 类域
二、实例化:从抽象到具体
2.1 实例化概念
2.2 对象大小
三、this指针:对象的“身份证”
四、C++和C语言实现Stack对比
总结:
完
前言:
在C++编程世界里,类和对象是面向对象编程(OOP)的核心概念,它们让我们能以更贴近现实的方式抽象和解决问题接下来,我们将从类的定义、实例化、this指针,以及C++与C语言实现数据结构的对比等方面,深入探索这一基石。
一、类的定义:封装的起点
类是对现实事物的抽象,它封装了数据(成员变量)和操作这些数据的函数(成员函数)。
1.1 类定义格式
class 类名 {// 访问限定符,如public、private、protected
public:// 成员函数声明或定义返回值类型 成员函数名(参数列表);// 成员变量声明数据类型 成员变量名;
};
// 若成员函数在类外定义,需通过作用域解析运算符::
返回值类型 类名::成员函数名(参数列表) {// 函数实现
}
例如,我们定义一个 Date 类:
class Date
{
public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;}void Printf(){cout << _year << '-' << _month <<'-' << _day << endl;}
private:// 为了区分成员变量,一般习惯上成员变量// 会加一个特殊标识,如_ 或者 m开头int _year; // year_ m_yearint _month;int _day;
};
1.2 访问限定符
- public(公有):类外可以直接访问。
- private(私有):仅类内可访问,是封装的关键,保护数据不被随意修改。
- protected(保护):类内和派生类可访问,多用于继承场景。
它们的作用域从自身出现的位置开始,到下一个访问限定符或类结束为止。
1.3 类域
类定义了一个作用域,类的成员(变量和函数)都在这个域内。在类外访问类的成员时,需要通过 类名::成员 的方式(如上述 Date::Printf ),或者通过对象( 对象.成员 、 对象->成员 ,后者用于指针)。
二、实例化:从抽象到具体
类是模板,实例化就是根据这个模板创建具体的对象。

2.1 实例化概念
比如 Date 类是模板,我们创建 Date p1; Date p2; , p1 和 p2 就是 Date 类的实例(对象)。每个对象都有自己独立的成员变量空间,而成员函数是共享的(因为函数代码不需要每个对象都存一份)。
2.2 对象大小
分析一下类对象中哪些成员呢?类实例化出的每个对象,都有独立的数据空间,所以对象中肯定包含成员变量,那么成员函数是否包含呢?首先函数被编译后是一段指令,对象中没办法存储,这些指令存储在一个单独的区域(代码段),那么对象中非要存储的话,只能是成员函数的指针。再分析一下,对象中是否有存储指针的必要呢,Date实例化d1和d2两个对象,d1和d2都有各自独立的成员变量_year/_month/_day存储各自的数据,但是d1和d2的成员函数Init/Print指针却是⼀样的,存储在对象中就浪费了。如果用Date实例化100个对象,那么成员函数指针就重复存储100次,太浪费了。这需要再额外哆嗦⼀下,其实函数指针是不需要存储的,函数指针是⼀个地址,调⽤函数被编译成汇编指令[call 地址], 其实编译器在编译链接时,就要找到函数的地址,不是在运⾏时找,只有动态多态是在运⾏时找,就需要存储函数地址。

上⾯我们分析了对象中只存储成员变量,C++规定类实例化的对象也要符合内存对齐的规则。
内存对齐规则
• 第⼀个成员在与结构体偏移量为0的地址处。
• 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
• 注意:对齐数 = 编译器默认的⼀个对齐数 与 该成员大小的较小值。
• VS中默认的对齐数为8
• 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
• 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
例如:
class A {
private:char c;int i;
};
char 占1字节, int 占4字节,内存对齐后, A 对象的大小是8字节( char 占1字节,后面补3字节对齐,再存 int 的4字节)。
三、this指针:对象的“身份证”
在C++中,每个非静态成员函数都隐含一个 this 指针参数,它指向当前调用该函数的对象。
比如在 Date 的 init 函数中, this 就指向调用 init 的那个 Date 对象
this 指针的特性:
- 隐含且自动传递,无需手动声明。
- 类型为 类类型* const ,不能修改 this 指针本身的指向。
- 空对象(如 Date* p = nullptr; )调用成员函数时,若函数内未访问成员变量,不会报错;若访问了,就会因解引用空指针而崩溃。
四、C++和C语言实现Stack对比
以栈(Stack)为例,对比C和C++的实现方式,更能体现类和对象的优势。
C语言实现Stack
typedef struct Stack {int* data;int top;int capacity;
} Stack;void StackInit(Stack* s, int capacity) {s->data = (int*)malloc(sizeof(int) * capacity);s->top = -1;s->capacity = capacity;
}void StackPush(Stack* s, int val) {// 扩容等逻辑...s->data[++s->top] = val;
}int StackPop(Stack* s) {return s->data[s->top--];
}void StackDestroy(Stack* s) {free(s->data);s->data = NULL;s->top = -1;s->capacity = 0;
}
使用时,需要手动管理每个函数的调用,且函数与结构分离,封装性弱。
C++实现Stack
class Stack {
public:Stack(int capacity) {data = new int[capacity];top = -1;this->capacity = capacity;}~Stack() {delete[] data;data = NULL;top = -1;capacity = 0;}void Push(int val) {// 扩容等逻辑...data[++top] = val;}int Pop() {return data[top--];}private:int* data;int top;int capacity;
};
使用时:
Stack s(10);
s.Push(1);
s.Push(2);
int val = s.Pop();
// 析构函数自动调用,无需手动释放内存
C++的实现将数据和操作封装在类内,通过构造函数、析构函数自动管理资源, private 保护了成员变量不被随意修改,代码更安全、易用,也更符合面向对象的思想。
总结:
综上,类和对象是C++面向对象编程的核心,它们通过封装、实例化和this指针等机制,让代码更具模块化、安全性和可读性。掌握这些概念,是迈进C++面向对象编程世界的关键一步。
