鱼C论坛

 找回密码
 立即注册
查看: 1931|回复: 13

分析一个c++程序的输出

[复制链接]
发表于 2023-5-11 09:28:41 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 6YKT6ZKm5LqI 于 2023-5-16 19:49 编辑

今天的c++实验要我们分析一段代码的输出,我调试了很久但是还是不明白代码的第46、47、48行,也就是pb[2].data、pb[1].data与pb[0].data的值,我的理解是pb[2].data、pb[1].data与pb[0].data应该输出派生类的data值,也就是12,11,10,但是运行结果一个也对不上,麻烦大家帮我看看

  1. #include <iostream>
  2. using namespace std;

  3. class Base
  4. {
  5. public:
  6.     Base() : data(count)
  7.     {
  8.         cout << "Base-ctor" << endl;
  9.         ++count;
  10.     }
  11.     ~Base()
  12.     {
  13.         cout << "Base-dtor" << endl;
  14.         --count;
  15.     }
  16.     static int count;
  17.     int data;
  18. };
  19. int Base::count;

  20. class Derived : public Base
  21. {
  22. public:
  23.     Derived() : data(count), data1(data)
  24.     {
  25.         cout << "Derived-ctor" << endl;
  26.         ++count;
  27.     }
  28.     ~Derived()
  29.     {
  30.         cout << "Derived-dtor" << endl;
  31.         --count;
  32.     }
  33.     static int count;
  34.     int data1;
  35.     int data;
  36. };
  37. int Derived::count = 10;

  38. int main()
  39. {
  40.     cout << sizeof(Base) << endl;   
  41.     cout << sizeof(Derived) << endl;
  42.     Base *pb = new Derived[3];  
  43.     cout << pb[2].data << endl;   
  44.     cout << pb[1].data << endl;   
  45.     cout << pb[0].data << endl;   
  46.     cout << ((static_cast<Derived *>(pb)) + 2)->data1 << endl;  
  47.     delete[] pb;   
  48.     cout << Base::count << endl;   
  49.     cout << Derived::count << endl;

  50.     return 0;
  51. }
复制代码

运行结果

运行结果

小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2023-5-11 09:29:18 | 显示全部楼层

回帖奖励 +4 鱼币

在这段代码中,首先定义了一个名为Base的基类,其中包含一个静态成员变量count和一个普通成员变量data。在构造函数中,data的值被初始化为count的值,然后count自增。在析构函数中,count自减。

接着定义了一个名为Derived的派生类,继承自Base类。Derived类中包含一个静态成员变量count和两个普通成员变量data和data1。在构造函数中,data的值被初始化为count的值,data1的值被初始化为data的值,然后count自增。在析构函数中,count自减。

在主函数中,先输出了Base和Derived的大小,然后定义了一个指向Derived类型的Base指针pb,并将其指向一个Derived类型的数组。接着输出了pb[2].data、pb[1].data和pb[0].data的值,这里需要注意的是,pb是一个指向Base类型的指针,因此pb[ i].data实际上是访问的Base类中的data成员,而不是Derived类中的data成员。而在Derived类中,data的初始值为10,因此pb[2].data、pb[1].data和pb[0].data的值分别为10、11、12。

接着输出了((static_cast<Derived *>(pb)) + 2)->data1的值,这里需要先将pb强制转换为Derived类型的指针,然后再加上2,即访问了第三个Derived对象的data1成员,其值为12。

最后释放了pb指向的内存,并输出了Base::count和Derived::count的值,其中Base::count为0,Derived::count为13。这是因为在构造函数中count自增,而在析构函数中count自减,因此最后的count值为13。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-5-11 09:31:16 | 显示全部楼层
isdkz 发表于 2023-5-11 09:29
在这段代码中,首先定义了一个名为Base的基类,其中包含一个静态成员变量count和一个普通成员变量data。在 ...

我也是这么理解的,可是pb[2].data、pb[1].data与pb[0].data的值从运行结果上看不是12、11、10
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-5-11 09:34:04 | 显示全部楼层
6YKT6ZKm5LqI 发表于 2023-5-11 09:31
我也是这么理解的,可是pb[2].data、pb[1].data与pb[0].data的值从运行结果上看不是12、11、10

看起来这是由于对象切片(object slicing)的问题。在C++中,对象切片发生在基类对象被赋值给派生类对象,或者基类指针或引用指向派生类对象时,会丢失派生类特有的数据和行为。

在你的代码中,你通过基类指针(Base*)访问派生类数组的元素(Derived[3]),这样就发生了对象切片,丢失了Derived部分的数据和行为,只保留了Base部分的数据和行为。

具体来说,当你访问pb[2].data,pb[1].data和pb[0].data时,你实际上访问的是Base部分的data,而不是你期望的Derived部分的data。也就是说,你看到的输出是基类的data值,而不是派生类的data值。

另外,你的派生类Derived和基类Base都有一个名为"data"的成员,这可能会引起混淆。当你通过基类指针访问"data"时,实际上是访问的基类的"data",而不是派生类的"data"。

如果你想要访问派生类的data,你需要将基类指针转换为派生类指针,然后再访问data。例如,你可以这样访问派生类的data:

  1. cout << static_cast<Derived*>(&pb[2])->data << endl;
复制代码


这样就可以访问到派生类的data值了。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-5-11 10:00:45 | 显示全部楼层
isdkz 发表于 2023-5-11 09:34
看起来这是由于对象切片(object slicing)的问题。在C++中,对象切片发生在基类对象被赋值给派生类对象 ...

但是如果访问的是基类data,pb[2].data、pb[1].data与pb[0].data的值应该是2、1、0,前两个变量的输出仍然与运行结果不匹配
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-5-11 10:02:51 | 显示全部楼层
6YKT6ZKm5LqI 发表于 2023-5-11 10:00
但是如果访问的是基类data,pb[2].data、pb[1].data与pb[0].data的值应该是2、1、0,前两个变量的输出仍 ...

你的理解是正确的,我要道歉,之前我没注意到这个问题。让我们再次深入研究一下这个问题。

在你的代码中,你创建了一个类型为Derived的动态数组,然后使用一个类型为Base的指针来引用它。虽然这在C++中是合法的,但是当你使用基类指针来操作派生类的数组时,往往会导致错误。

具体来说,当你通过基类指针访问数组元素(如pb[2]、pb[1]和pb[0])时,C++会假设每个数组元素的大小与基类的大小相同。然而,实际上,派生类的大小可能比基类大(特别是当派生类添加了新的成员变量或函数时)。因此,当你通过基类指针访问派生类数组的元素时,C++可能会错误地计算数组元素的位置,从而导致错误的结果。

在你的代码中,Base类的大小是8字节(一个int成员变量和一个虚函数指针),而Derived类的大小是16字节(因为它增加了两个int成员变量)。因此,当你通过基类指针访问pb[2]、pb[1]和pb[0]时,C++实际上是访问了内存中错误的位置。

简而言之,你不应该通过基类指针来操作派生类的数组。如果你需要操作的是派生类的数组,你应该使用派生类的指针。如果你需要通过基类指针来操作对象,你应该创建基类的指针数组,而不是基类指针的数组。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-5-11 17:47:33 | 显示全部楼层

回帖奖励 +4 鱼币

小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2023-5-11 18:24:19 | 显示全部楼层

回帖奖励 +4 鱼币

小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-5-11 18:24:29 | 显示全部楼层

回帖奖励 +4 鱼币

bibi
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2023-5-11 18:29:31 From FishC Mobile | 显示全部楼层

回帖奖励 +4 鱼币

币拿走
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-5-11 18:51:14 | 显示全部楼层
本帖最后由 yinda_peng 于 2023-5-11 18:52 编辑

不会咧
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-5-11 18:52:12 | 显示全部楼层
@夏季的春秋 你来看,我不会C++
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-5-11 20:50:33 | 显示全部楼层
c++没学过,不会捏
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-5-13 13:54:28 | 显示全部楼层
在C++中,派生类对象的布局被编译器决定,因此在不同的编译器、操作系统及计算机架构下,表现可能不同。

对于给定的代码片段,由于动态分配内存的方式是使用指向派生类对象的基类指针,因此在访问 pb[2].data、pb[1].data 和 pb[0].data 时,只会访问到对象中第一个 Base 类成员变量 data。而这些对象在内存中的布局将取决于特定的编译器实现,所以无法确切预测这些数据成员的值。

但是可以确定的是,因为 Derived 类型对象的内存空间大于 Base 类型对象的内存空间,在向一个指向 Base 类型的指针赋值为指向 Derived 对象的指针时,最后一块空闲区域可能没有被完全填充(或者说未被正确地重新初始化)。因此,如果将 pb 转换为 Derived* 指针,则 ((static_cast<Derived *>(pb)) + 2)->data1 的值可能会受到初始值的影响,而这个值也没有办法精确预测。

小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-6-10 15:16

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表