《C++初阶之类和对象》【类 + 类域 + 访问限定符 + 对象的大小 + this指针】
类(Class):是对具有相同 属性(数据)和方法(行为)的一组对象的抽象描述类是 C++ 的核心特性之一,用于实现 面向对象编程(OOP)中的封装类是对象的蓝图,定义了对象的属性(数据成员)和行为(成员函数)一个形象的例子:比如 “人类”,可以有姓名、年龄等属性,以及说话、行走等行为,在编程里就可据此定义一个 “人类” 的类。代码语言:javascriptAI代码解释public:age = a
什么是类?
类(Class):是对具有相同属性(数据)和方法(行为)的一组对象的抽象描述
- 类是 C++ 的核心特性之一,用于实现
面向对象编程(OOP)中的封装 - 类是对象的蓝图,定义了对象的 属性(数据成员) 和 行为(成员函数)
一个形象的例子:比如 “人类”,可以有姓名、年龄等属性,以及说话、行走等行为,在编程里就可据此定义一个 “人类” 的类。
怎么定义类?
代码语言:javascript
AI代码解释
class Person
{
public:
void setAge(int a)
{
age = a;
}
int getAge()
{
return age;
}
private:
};
C++使用
class关键字定义,类中包含以下三部分的内容。 类的基本结构:关键的三个组成部分
- 数据成员(属性):类的变量(如:
int age;) - 成员函数(方法):类的函数(如:
void setAge(int a) 和 int getAge()) - 访问修饰符:
public:任何地方可访问protected:类及其子类可访问private:仅类内部访问(默认)
关于类需要注意什么?
下面博主挑选了四个关于“类”需要注意的事项,它们分别是关于:
- 类定义的语法规范
- 成员变量的命名约定
struct与class的区别- 类内定义的成员函数与内联
1. 类定义的语法规范:
分号结尾:类定义结束时必须使用分号 ;,这是 C++ 语法的强制要求。
代码语言:javascript
AI代码解释
class MyClass // 类定义开始
{
// 成员...
}; // 分号不可省略!
2. 成员变量的命名约定:
命名前缀 / 后缀:为避免成员变量与局部变量或函数参数重名,通常添加特殊标识:
代码语言:javascript
AI代码解释
class Person
{
private:
int m_age; // m_前缀(Microsoft风格)
std::string _name; // 前缀(常见于开源项目)
double salary_; // 后缀(Google风格)
};
注意:C++ 标准并未强制要求此规则,具体风格需遵循团队或项目规范。
3. struct 与 class 的区别:
|
特性 |
struct |
class |
|---|---|---|
|
默认访问权限 |
public |
private |
|
设计哲学 |
数据聚合(POD, Plain Old Data) |
封装复杂逻辑(OOP) |
|
常见用途 |
轻量级数据容器、C 兼容结构 |
封装、继承、多态 |
示例对比:
代码语言:javascript
AI代码解释
struct Point
{
int x, y; // 默认public
void move(int dx, int dy); //注意:可定义函数
};
class Rectangle
{
int width, height; // 默认private
public:
int area() const
{
return width * height;
}
};
4. 类内定义的成员函数与内联:
隐式内联:在类内部直接定义的成员函数会被编译器视为 inline 函数(但最终是否内联由编译器决定)
代码语言:javascript
AI代码解释
class Math
{
public:
int add(int a, int b) // 隐式内联
{
return a + b;
}
};
显式内联:若在类外定义函数,需显式使用 inline 关键字(通常在头文件中):
代码语言:javascript
AI代码解释
// Math.h
class Math
{
public:
int multiply(int a, int b); // 声明
};
// Math.cpp
inline int Math::multiply(int a, int b) // 显式内联
{
return a * b;
}
注意事项:
- 内联函数的定义必须在每个调用它的翻译单元(.cpp 文件)中可见,因此通常直接放在头文件中。
- 现代编译器会智能优化内联决策,即使未声明
inline,简单函数也可能被自动内联。
C++中的struct关键字有什么变化?
刚才我们有提到使用关键字struct来定义类,相比C++中的struct和C语言中的struct已经不太一样了吧,那两者又有什么区别呢?
代码语言:javascript
AI代码解释
include<iostream>
using namespace std;
/*
* C++ 中对 struct 的升级:
* 1. struct 现在是一个完全的类(class),内部可以定义成员函数
* 2. struct 名称本身可以直接作为类型名使用(不再需要 typedef)
*/
// ------------------------- C 语言风格的链表节点 -------------------------
// C 中需要 typedef 简化类型名称
typedef struct SingleListNodeC
{
int val;
struct ListNodeC* next; // C 中必须带 struct 关键字
}SLTNode; // 通过 typedef 定义类型别名 LTNode
// ------------------------- C++ 风格的链表节点 -------------------------
// C++ 中 struct 本身就是类型名,且支持成员函数
struct SingleListNodeCPP
{
// 成员函数:初始化节点
void Init(int x)
{
val = x; // 设置节点值
next = nullptr; // 初始化指针为空
}
// 成员变量
int val;
SingleListNodeCPP* next; // 直接使用 SingleListNodeCPP 作为类型(无需 struct 关键字)
};
/*
* 对比说明:
* 1. C++ 的 struct 默认所有成员为 public(与 class 的唯一区别)
* 2. 完全支持构造函数、析构函数等类特性
* 3. 兼容 C 的语法,但更推荐 C++ 风格
*/
int main()
{
// C 风格用法(兼容模式)
SLTNode node_c;
node_c.val = 10;
node_c.next = NULL;
// C++ 风格用法
SingleListNodeCPP node_cpp;
node_cpp.Init(20); // 调用成员函数初始化
return 0;
}
|
对比维度 |
C 语言风格 struct |
C++ 风格 struct |
|---|---|---|
|
类型定义方式 |
需用typedef定义类型别名简化使用,定义时节点类型前需加struct关键字 |
struct名称本身就是类型名,可直接使用,无需typedef和额外的struct关键字 |
|
成员函数支持 |
仅能定义数据成员,不支持成员函数 |
可定义成员函数,如案例中SingleListNodeCPP的Init函数用于初始化节点 |
|
访问权限默认值 |
无明确访问权限概念(类似 C++ 中public ,所有成员都可直接访问 ) |
默认成员访问权限为public(与class不同,class默认private ) |
使用类有哪些好处?
接下来我们就使用C++的类再次重新实现一下我们在《数据结构初阶》中实现的栈这种数据结构,直观的感受一下使用类实现和之前实现的区别:
代码语言:javascript
AI代码解释
#include<iostream>
#include<cstdlib>
#include<cassert>
using namespace std;
// 定义一个栈(Stack)类,基于动态数组实现
class Stack
{
public:
/*-------------------------栈的“初始化”操作-------------------------*/
/*
* 初始化栈,默认初始容量为4
* 参数n:可选参数,指定初始容量,默认为4
*/
void Init(int n = 4)
{
//申请n个int大小的内存空间
array = (int*)malloc(n * sizeof(int));
// 检查内存是否申请成功
if (array==nullptr)
{
perror("malloc申请空间失败");
return;
}
// 初始化栈的容量和栈顶指针
capacity = n;
top = 0; //top指向下一个可插入的位置
}
/*-------------------------栈的“入栈”操作-------------------------*/
void Push(int x)
{
// 这里应该添加扩容逻辑,当top == capacity时需要扩容
// 当前实现没有扩容功能,会导致数组越界
//
array[top++] = x; // 元素放入数组,然后top指针后移
}
/*-------------------------栈的“获取栈顶元素”操作-------------------------*/
int Top()
{
assert(top > 0); //断言检查栈是否为空
return array[top - 1];
}
/*-------------------------栈的“销毁”操作-------------------------*/
void Destroy()
{
free(array); // 释放动态数组
array = nullptr; // 指针置空,防止野指针
top = capacity = 0; // 重置容量和栈顶指针
}
private:
/*---------------------成员变量---------------------*/
int* array; // 动态数组指针,存储栈元素
int capacity; // 栈的容量
int top; // 栈顶指针(指向下一个可插入位置)
};
int main()
{
Stack st; // 创建栈对象
st.Init(); // 初始化栈(使用默认容量4)
st.Push(1); // 压入元素1
st.Push(2); // 压入元素2
// 输出栈顶元素(应该是2)
cout << st.Top() << endl;
st.Destroy(); // 销毁栈,释放资源
return 0;
}
这里我们最能明显体会到的区别有以下三点:
|
维度 |
C语言实现 |
C++类实现 |
|---|---|---|
|
数据与操作 |
分离(结构体+独立函数) |
整合(成员变量+成员方法) |
|
实例表示 |
显式传递结构体指针(Stack*) |
隐含this指针(st.Push()) |
|
访问控制 |
无封装(所有字段可被外部直接修改) |
通过private保护关键数据 |
---------------类的实例化---------------
什么是类的实例化?
类的实例化:是指根据类的定义创建具体对象的过程。
- 实例化后,对象会获得类中定义的成员变量(数据)和成员函数(行为),并占用实际的内存空间。
实例化的核心概念:
类(Class):是对象的蓝图或模板,仅定义结构,不占用内存。对象(Object):是类的具体实例,占用内存,存储实际数据。
类比:
- 类 ≈ 建筑设计图
- 对象 ≈ 按设计图建造的房子
在面向对象编程中,一个类可以实例化出多个对象,这些实例化出的对象会占用实际的物理空间,用于存储类成员变量。 类与对象的关系就好比建筑设计图与实际建筑:建筑设计图详细规划了房屋的结构,如房间数量、空间布局、功能分区等,但它只是概念性的图纸,并不具备实体形态,无法供人居住或使用;只有依照设计图施工建造出实际的房屋,才能满足居住、办公等实际需求。 同理,类是对数据和行为的抽象描述,定义了成员变量和成员函数,但它本身并不占据物理内存,也无法存储实际数据,仅作为对象创建的模板。 而通过类实例化得到的对象,就像建造完成的房屋,会在计算机内存中分配实际的空间,用于存储类所定义的成员变量的值。
- 例如:通过 “学生” 类可以实例化出张三、李四等具体学生对象,每个对象都有自己独立的存储空间,用于存放各自的姓名、年龄、成绩等成员变量数据,且不同对象的同一成员变量可以存储不同的值。
- 多个对象之间相互独立,各自存储和管理自己的数据,却又共享类所定义的行为和操作规范,使得程序能够高效地处理复杂的现实场景。
更多推荐



所有评论(0)