当前位置: 首页 > news >正文

类与对象(1)

1 类的引入

C++ 在 C 语言结构体的基础上引入了 类(class) 概念,用于实现面向对象编程。类将 数据(成员变量) 和 操作数据的方法(成员函数) 封装成一个整体,并支持访问控制、继承和多态等特性。

2 类的定义

2.1 核心结构

class ClassName {access_specifier:  // 访问限定符member_variables;   // 成员变量member_functions(); // 成员函数// 可包含多个访问限定区块
};
  • 通过 class 或 struct 关键字定义类(默认访问权限不同):
class ClassName {  // class 默认 private 访问// 成员变量和成员函数
};struct StructName { // struct 默认 public 访问// 成员变量和成员函数
};

示例: 

class Person {
public: // 公有成员void setName(std::string name) { _name = name; }void printInfo() { std::cout << "Name: " << _name << ", Age: " << _age << std::endl;}
private: // 私有成员std::string _name;int _age = 0; // C++11 支持成员变量初始化
};

2.2 关键要素

组成部分说明示例
类关键字class (默认private) 或 struct (默认public)class Student
成员变量描述对象状态的变量std::string name;
成员函数操作成员变量的函数void printInfo() { ... }
访问限定符控制访问权限的标签public:private:

2.3 成员函数定义

// 声明和定义分离
class Rectangle {
public:double area();  // 声明
private:double width, height;
};// 类外定义成员函数
double Rectangle::area() { return width * height; 
}// 声明和定义合并
class Circle {
public:double area() { return 3.14 * radius * radius; }
private:double radius;
};

3 类的封装

限定符类内访问类外访问继承访问典型用途
public保持原权限对外接口
protected子类可访问继承体系共享成员
private子类不可访问隐藏实现细节

封装的核心思想:将数据和操作数据的方法绑定,并隐藏内部实现细节(通过 private/protected)仅暴露必要接口(public)

3.1 封装实践:

class BankAccount {
public:// 公有接口void deposit(double amount) { if(amount > 0) balance += amount; }double getBalance() const { return balance; }private:// 隐藏实现细节double balance = 0; std::string accountId;
};// 使用
BankAccount acc;
acc.deposit(100);      // ✅ 允许
// acc.balance = 1000; // ❌ 编译错误:private成员

3.2 类的作用域

类定义了一个独立的作用域:

  • 成员变量/函数在类内直接通过名字访问。
  • 类外需使用作用域解析符 :: 访问静态成员或定义成员函数。
void Person::setName(std::string name) { // 类外定义成员函数_name = name;
}

3.3 类的实例化

通过类创建对象的过程:

Person p1;               // 栈上实例化
Person* p2 = new Person; // 堆上实例化
  • 实例化时系统为对象分配内存。
  • 对象生命周期:
  1. 栈对象:作用域结束时自动销毁。
  2. 堆对象:需手动 delete 释放。

4 类的大小及存储

4.1 大小

类的大小由成员变量决定(遵循内存对齐规则),与成员函数无关:

  • 空类大小为 1 字节(占位标识)。
  • 内存对齐原则:
  1. 成员偏移量为自身大小的整数倍。
  2. 第一个成员在结构体偏移量为0的地址处。
  3. 对齐数==编译器默认对齐数与该成员大小的较小值。
  4. 总大小为最大对齐数的整数倍。
  5. 虚函数会增加虚表指针(通常 4/8 字节)。
class A {char c;     // 1 字节 → 首元素,对齐到结构体偏移量0处int i;      // 4 字节→ ,对齐到偏移量为4处
}; // 大小 = 8 字节(char:1 + 3 填充为4字节)class B {int i;      // 4 字节double d;   // 8 字节,对齐数默认为8 → 偏移量需是 8 的倍数,故前面补 4 字节,到这占用了16个字节char c;     // 1 字节 →虽然对齐数是1,在16后对齐1个字节,但 总大小需是 8 的倍数,末尾补 7 字节
}; // 大小 = 24 字节(4 + 4 :在double d前int i后填充4字节→16字节;在char c后填充 7字节,16+1+7 = 24)

4.2 存储

内存分布:

成员类型存储位置说明
非静态成员变量对象内存空间每个对象独立副本
静态成员变量全局数据区所有对象共享
成员函数代码段所有对象共享同一份代码
虚函数虚表(vtable)每个类一个虚表,对象含vptr

5 类成员函数的 this 指针

5.1 核心机制

  • 每个 非静态成员函数 隐含一个 this 指针参数(编译器自动添加)。
  • this 指向调用该函数的对象。
  • 通过 this 访问对象的成员变量/函数。

5.2 关键特性

  • 隐式传递:函数调用 obj.func(x) 被转换为 func(&obj, x)
  • 类型约束this 是 ClassName* const 类型(常量指针)。
  • 显式使用:在成员函数中可直接使用 this
void Person::setName(std::string name) {this->_name = name; // 明确指明成员变量
}
特性说明
隐式存在编译器自动添加到每个非静态成员函数的参数列表首部
类型固定ClassName* const (顶层const指针)
指向当前对象始终指向调用该成员函数的对象
不可修改性指针值不可修改 (this = nullptr 非法)
访问控制桥梁通过this指针访问对象的所有成员(包括private)
静态函数无this静态成员函数不能使用this指针

5.3 链式调用

Person& Person::setAge(int age) {_age = age;return *this; // 返回当前对象的引用
}
p.setAge(20).setName("Alice"); // 链式调用

5.4 空指针访问实验

class Test {
public:void safeFunc() { std::cout << "Safe function\n"; }void unsafeFunc() { std::cout << value;  // 访问成员变量}static void staticFunc() {std::cout << "Static function\n";}
private:int value;
};Test* ptr = nullptr;
ptr->safeFunc();     // ✅ 正常运行:不访问成员变量
ptr->staticFunc();   // ✅ 正常运行:静态函数无this指针
// ptr->unsafeFunc(); // ❌ 崩溃:访问成员需要this指针

关键结论:

  • 空指针可调用不访问成员变量的函数(包括静态函数)
  • 访问成员变量时需通过有效的this指针,空指针导致崩溃

注意事项

  • 静态成员函数无 this 指针(不能访问非静态成员)。
  • this 指针作为形参存在栈上or寄存器上。
  • this 指针在成员函数开始执行前构造,结束后销毁。

6 总结

概念关键点
类定义

class(默认 private)/struct(默认 public)封装数据与函数

访问限定符publicprotectedprivate 控制封装性
类作用域类外定义成员函数需用 ClassName::
实例化栈对象自动管理,堆对象需手动 new/delete
类大小由成员变量决定(内存对齐),不含成员函数和静态成员
this 指针隐含指向当前对象的常量指针,实现成员访问和链式调用
http://www.xdnf.cn/news/928639.html

相关文章:

  • 物联网技术发展与应用研究分析
  • 技巧小结:根据寄存器手册写常用外设的驱动程序
  • 6.7-leetcodeT3170
  • 低成本嵌入式Linux开发方案:RV1106入门
  • 代码注释类型
  • 【win | 自动更新关闭】win11
  • 解决使用nvm安装node报错或者安装后有node没有npm
  • 基于投影寻踪博弈论-云模型的综合评价
  • 设计一套流程引擎队列分发器
  • 2025年AI编程工具推荐
  • 外部排序全解析:从基础到优化策略(王道)
  • go工具库:hertz api框架 hertz client的使用
  • 无线网络扫描与分析工具 LizardSystems Wi-Fi Scanner 25.05
  • 【python深度学习】Day 47 注意力热图可视化
  • 蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析
  • transformers 的Trainer的用法
  • Cloudflare 免费域名邮箱 支持 Catch-all 无限别名收件
  • JAVA理论第四战-线程池
  • 【AI论文】反思、重试、奖励:通过强化学习实现大型语言模型的自我提升
  • archlinux中使用 Emoji 字体
  • keil 5打开编译keil 4解决方案,兼容exe查找下载
  • 编程关键字
  • 【区块链基础】区块链的 Fork(分叉)深度解析:原理、类型、历史案例及共识机制的影响
  • 分类与扩展
  • 【推荐算法】推荐算法演进史:从协同过滤到深度强化学习
  • 「Java基本语法」代码格式与注释规范
  • 第二十七课:手搓梯度提升树
  • AI掘金时代:探讨如何用价值杠杆撬动付费用户增长
  • 记录下three.js学习过程中不理解问题①
  • 测试(面经 八股)