news 2026/4/3 19:14:21

类和对象(上)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
类和对象(上)

类和对象(上)

类定义格式

• class为定义类的关键字,Stack为类的名字,{}中为类的主体,注意类定义结束时后⾯分号不能省略。类体中内容称为类的成员:类中的变量称为类的属性或成员变量; 类中的函数称为类的⽅法或者成员函数。

• 为了区分成员变量,⼀般习惯上成员变量会加⼀个特殊标识,如成员变量前⾯或者后⾯加_ 或者 m开头,注意C++中这个并不是强制的,只是⼀些惯例,具体看公司的要求。

• C++中struct也可以定义类,C++兼容C中struct的⽤法,同时struct升级成了类,明显的变化是struct中可以定义函数,⼀般情况下我们还是推荐⽤class定义类。

• 定义在类⾯的成员函数默认为inline。

#include<iostream> using namespace std; class Stack { public: // 成员函数 void Init(int n = 4) { array = (int*)malloc(sizeof(int) * n); if (nullptr == array) { perror("malloc申请空间失败"); return; } capacity = n; top = 0; } void Push(int x) { // ...扩容 array[top++] = x; } 27 int Top() 28 { 29 assert(top > 0); 30 31 return array[top - 1]; 32 } 33 34 void Destroy() 35 { 36 free(array); 37 array = nullptr; 38 top = capacity = 0; 39 } 40 41 private: 42 // 成员变量 43 int* array; 44 size_t capacity; 45 size_t top; 46 }; // 分号不能省略 47 48 int main() 49 { 50 Stack st; 51 st.Init(); 52 st.Push(1); 53 st.Push(2); 54 55 cout << st.Top() << endl; 56 57 st.Destroy(); 58 59 return 0; 60 }

访问限定符

• C++⼀种实现封装的⽅式,⽤类将对象的属性与⽅法结合在⼀块,让对象更加完善,通过访问权限选择性的将其接⼝提供给外部的⽤⼾使⽤。

• public修饰的成员在类外可以直接被访问;protected和private修饰的成员在类外不能直接被访问,protected和private是⼀样的,以后继承章节才能体现出他们的区别。

• 访问权限作⽤域从该访问限定符出现的位置开始直到下⼀个访问限定符出现时为⽌,如果后⾯没有访问限定符,作⽤域就到 }即类结束。

• class定义成员没有被访问限定符修饰时默认为private,struct默认为public。

• ⼀般成员变量都会被限制为private/protected,需要给别⼈使⽤的成员函数会放为public。

类域

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

• 类域影响的是编译的查找规则,下⾯程序中Init如果不指定类域Stack,那么编译器就把Init当成全局函数,那么编译时,找不到array等成员的声明/定义在哪⾥,就会报错。指定类域Stack,就是知道Init是成员函数,当前域找不到的array等成员,就会到类域中去查找。

#include<iostream> using namespace std; class Stack { public: // 成员函数 void Init(int n = 4); private: // 成员变量 int* array; size_t capacity; size_t top; }; // 声明和定义分离,需要指定类域 void Stack::Init(int n) { array = (int*)malloc(sizeof(int) * n); if (nullptr == array) { perror("malloc申请空间失败"); return; } capacity = n; top = 0; } int main() { Stack st; st.Init(); return 0; }

上⾯的程序运⾏后,我们看到没有成员变量的B和C类对象的⼤⼩是1,为什么没有成员变量还要给1个字节呢?因为如果⼀个字节都不给,怎么表⽰对象存在过呢!所以这⾥给1字节,纯粹是为了占位标识对象存在。

内存对⻬规则

• 第⼀个成员在与结构体偏移量为0的地址处。

• 其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处。

• 注意:对⻬数 = 编译器默认的⼀个对⻬数 与 该成员⼤⼩的较⼩值。

• VS中默认的对⻬数为8

• 结构体总⼤⼩为:最⼤对⻬数(所有变量类型最⼤者与默认对⻬参数取最⼩)的整数倍。

• 如果嵌套了结构体的情况,嵌套的结构体对⻬到⾃⼰的最⼤对⻬数的整数倍处,结构体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体的对⻬数)的整数倍。

内存对齐效率更高一点,因为如果要单独读取某个数据的时候只需要读取一遍

this指针

• Date类中有 Init 与 Print 两个成员函数,函数体中没有关于不同对象的区分,那当d1调⽤Init和Print函数时,该函数是如何知道应该访问的是d1对象还是d2对象呢?那么这⾥就要看到C++给了⼀个隐含的this指针解决这⾥的问题

• 编译器编译后,类的成员函数默认都会在形参第⼀个位置,增加⼀个当前类类型的指针,叫做this指针。⽐如Date类的Init的真实原型为, void Init(Date* const this, int year,int month, int day)

• 类的成员函数中访问成员变量,本质都是通过this指针访问的,如Init函数中给_year赋值, this->_year = year;

• C++规定不能在实参和形参的位置显⽰的写this指针(编译时编译器会处理),但是可以在函数体内显⽰使⽤this指针。

#include<iostream> using namespace std; class Date { public: // void Init(Date* const this, int year, int month, int day) void Init(int year, int month, int day) { // 编译报错:error C2106: “=”: 左操作数必须为左值 // this = nullptr; // this->_year = year; _year = year; this->_month = month; this->_day = day; } void Print() { cout << _year << "/" << _month << "/" << _day << endl; } private: // 这⾥只是声明,没有开空间 int _year; int _month; int _day; }; int main() { // Date类实例化出对象d1和d2 Date d1; Date d2; // d1.Init(&d1, 2024, 3, 31); d1.Init(2024, 3, 31); d1.Print(); d2.Init(2024, 7, 5); d2.Print(); return 0; }

这段代码看起来有问题,但让我先分析一下:

int main() { A* p = nullptr; // 创建一个指向A类的空指针 p->Print(); // 通过空指针调用成员函数 return 0; }

代码分析

问题所在:

  • p被初始化为nullptr(空指针)
  • p->Print()试图通过空指针调用成员函数

运行结果

理论上:这会导致未定义行为,通常会导致程序崩溃(段错误)

但实际上可能运行成功的原因:

  1. 成员函数的调用机制p->Print()在编译时被转换为A::Print(p)
  2. Print函数没有访问成员变量:函数内部没有使用this指针访问数据成员
  3. 编译器优化:由于不需要访问对象数据,函数可以正常执行

验证测试

class A { public: void Print() { cout << "A::Print()" << endl; // 如果加上这行,就会崩溃: // cout << _a << endl; // 访问成员变量,需要有效的this指针 } void PrintWithData() { cout << "Data: " << _a << endl; // 这里会崩溃! } private: int _a = 10; }; int main() { A* p = nullptr; p->Print(); // 可能正常运行 // p->PrintWithData(); // 一定会崩溃! return 0; }

重要结论

  • 这是未定义行为,不要在实际代码中这样写
  • 虽然可能"侥幸"运行成功,但依赖于编译器和具体实现
  • 安全的做法:确保指针不为空再调用成员函数

这是一种危险的编程实践,应该避免!

this指针存在内存哪个区域的 ()

A. 栈 B.堆 C.静态区 D.常量区 E.对象⾥⾯

正确答案是:A. 栈

详细解释

this指针的本质:

  • this是一个隐式的函数参数
  • 当调用成员函数时,编译器自动将对象的地址作为第一个参数传递

示例说明

class MyClass { public: void print() { // 编译器实际上将 this 作为第一个参数传递 // void print(MyClass* this); } }; int main() { MyClass obj; obj.print(); // 相当于 print(&obj); MyClass* ptr = new MyClass(); ptr->print(); // 相当于 print(ptr); return 0; }

内存区域分析

  1. this指针本身作为函数参数存储在栈中 ✓
  2. :如果对象是用new创建的,对象本身在堆中,但this指针仍在栈中
  3. 静态区:存储全局变量和静态变量
  4. 常量区:存储字符串常量等
  5. 对象里面:this指向对象,但本身不在对象内部

函数调用时的内存布局

栈帧(stack frame): | 返回地址 | | 参数n | | ... | | 参数1 | ← this指针在这里(作为第一个隐式参数) | 局部变量|

总结:this指针是成员函数的隐式参数,与其他函数参数一样存储在栈中。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/2 21:41:25

HyperLPR3 车牌识别(python3)

HyperLPR已经更新到了v3的版本&#xff0c;该版本与先前的版本一样都是用于识别中文车牌的开源图像算法项目&#xff0c;最新的版本的源码可从github中提取&#xff1a;https://github.com/szad670401/HyperLPR一、安装扩展 python -m pip install hyperlpr3 https://pypi.tuna…

作者头像 李华
网站建设 2026/4/2 12:14:38

234回文链表

2025_12_14 链表简单&#xff08;虽然是简单但是链表的我总是卡呢&#xff09; 234回文链表 思路&#xff1a;我想到的是递归或者倒转一半或者栈&#xff0c;再遍历检查回文&#xff0c;但是限制了空间就只能倒转一半&#xff0c;感觉写起来好麻烦www感觉写的不是很优雅&#x…

作者头像 李华
网站建设 2026/4/2 14:23:06

JS核心语法

特性varletconst块级作用域❌&#xff08;函数级作用域&#xff09;✅✅变量提升✅&#xff08;可先使用后声明&#xff09;❌&#xff08;暂时性死区&#xff09;❌&#xff08;暂时性死区&#xff09;重复声明✅❌❌重新赋值✅✅❌&#xff08;声明时必须赋值&#xff09;// …

作者头像 李华
网站建设 2026/4/1 14:44:29

分公司组织架构图在线设计 总部分支管理模板

良功绘图网站 (https://www.lghuitu.com ) 在企业规模化发展的进程中&#xff0c;分公司的设立成为拓展市场、优化资源配置的重要举措。而总部分支之间的高效协同&#xff0c;离不开清晰、科学的组织架构作为支撑。分公司组织架构图作为直观呈现管理层级、部门设置、权责划分的…

作者头像 李华
网站建设 2026/3/28 4:20:32

KD-Tree的查询原理

好的&#xff0c;让我详细解释KD-Tree的查询原理&#xff0c;以及为什么它能将时间复杂度从O(n)降到O(log n)。 KD-Tree的基本结构 KD-Tree&#xff08;k-dimensional tree&#xff09;是一种用于多维空间的数据结构&#xff0c;特别适合范围搜索和最近邻搜索。 构建过程示例…

作者头像 李华
网站建设 2026/3/25 9:45:12

基于Mask R-CNN的道路路面损伤自动检测与分类研究

1. 基于Mask R-CNN的道路路面损伤自动检测与分类研究 1.1. 引言 随着城市化进程的加速&#xff0c;道路基础设施的维护变得越来越重要。传统的人工检测方法效率低下、成本高昂&#xff0c;且存在安全隐患。&#x1f6a7; 近年来&#xff0c;计算机视觉技术的快速发展为道路路…

作者头像 李华