分析一个c++程序的输出
本帖最后由 6YKT6ZKm5LqI 于 2023-5-16 19:49 编辑今天的c++实验要我们分析一段代码的输出,我调试了很久但是还是不明白代码的第46、47、48行,也就是pb.data、pb.data与pb.data的值,我的理解是pb.data、pb.data与pb.data应该输出派生类的data值,也就是12,11,10,但是运行结果一个也对不上,麻烦大家帮我看看
#include <iostream>
using namespace std;
class Base
{
public:
Base() : data(count)
{
cout << "Base-ctor" << endl;
++count;
}
~Base()
{
cout << "Base-dtor" << endl;
--count;
}
static int count;
int data;
};
int Base::count;
class Derived : public Base
{
public:
Derived() : data(count), data1(data)
{
cout << "Derived-ctor" << endl;
++count;
}
~Derived()
{
cout << "Derived-dtor" << endl;
--count;
}
static int count;
int data1;
int data;
};
int Derived::count = 10;
int main()
{
cout << sizeof(Base) << endl;
cout << sizeof(Derived) << endl;
Base *pb = new Derived;
cout << pb.data << endl;
cout << pb.data << endl;
cout << pb.data << endl;
cout << ((static_cast<Derived *>(pb)) + 2)->data1 << endl;
delete[] pb;
cout << Base::count << endl;
cout << Derived::count << endl;
return 0;
}
在这段代码中,首先定义了一个名为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.data、pb.data和pb.data的值,这里需要注意的是,pb是一个指向Base类型的指针,因此pb[ i].data实际上是访问的Base类中的data成员,而不是Derived类中的data成员。而在Derived类中,data的初始值为10,因此pb.data、pb.data和pb.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。 isdkz 发表于 2023-5-11 09:29
在这段代码中,首先定义了一个名为Base的基类,其中包含一个静态成员变量count和一个普通成员变量data。在 ...
我也是这么理解的,可是pb.data、pb.data与pb.data的值从运行结果上看不是12、11、10 6YKT6ZKm5LqI 发表于 2023-5-11 09:31
我也是这么理解的,可是pb.data、pb.data与pb.data的值从运行结果上看不是12、11、10
看起来这是由于对象切片(object slicing)的问题。在C++中,对象切片发生在基类对象被赋值给派生类对象,或者基类指针或引用指向派生类对象时,会丢失派生类特有的数据和行为。
在你的代码中,你通过基类指针(Base*)访问派生类数组的元素(Derived),这样就发生了对象切片,丢失了Derived部分的数据和行为,只保留了Base部分的数据和行为。
具体来说,当你访问pb.data,pb.data和pb.data时,你实际上访问的是Base部分的data,而不是你期望的Derived部分的data。也就是说,你看到的输出是基类的data值,而不是派生类的data值。
另外,你的派生类Derived和基类Base都有一个名为"data"的成员,这可能会引起混淆。当你通过基类指针访问"data"时,实际上是访问的基类的"data",而不是派生类的"data"。
如果你想要访问派生类的data,你需要将基类指针转换为派生类指针,然后再访问data。例如,你可以这样访问派生类的data:
cout << static_cast<Derived*>(&pb)->data << endl;
这样就可以访问到派生类的data值了。 isdkz 发表于 2023-5-11 09:34
看起来这是由于对象切片(object slicing)的问题。在C++中,对象切片发生在基类对象被赋值给派生类对象 ...
但是如果访问的是基类data,pb.data、pb.data与pb.data的值应该是2、1、0,前两个变量的输出仍然与运行结果不匹配 6YKT6ZKm5LqI 发表于 2023-5-11 10:00
但是如果访问的是基类data,pb.data、pb.data与pb.data的值应该是2、1、0,前两个变量的输出仍 ...
你的理解是正确的,我要道歉,之前我没注意到这个问题。让我们再次深入研究一下这个问题。
在你的代码中,你创建了一个类型为Derived的动态数组,然后使用一个类型为Base的指针来引用它。虽然这在C++中是合法的,但是当你使用基类指针来操作派生类的数组时,往往会导致错误。
具体来说,当你通过基类指针访问数组元素(如pb、pb和pb)时,C++会假设每个数组元素的大小与基类的大小相同。然而,实际上,派生类的大小可能比基类大(特别是当派生类添加了新的成员变量或函数时)。因此,当你通过基类指针访问派生类数组的元素时,C++可能会错误地计算数组元素的位置,从而导致错误的结果。
在你的代码中,Base类的大小是8字节(一个int成员变量和一个虚函数指针),而Derived类的大小是16字节(因为它增加了两个int成员变量)。因此,当你通过基类指针访问pb、pb和pb时,C++实际上是访问了内存中错误的位置。
简而言之,你不应该通过基类指针来操作派生类的数组。如果你需要操作的是派生类的数组,你应该使用派生类的指针。如果你需要通过基类指针来操作对象,你应该创建基类的指针数组,而不是基类指针的数组。 币 sfqxx 发表于 2023-5-11 17:47
币
啊对 bibi 币拿走 本帖最后由 yinda_peng 于 2023-5-11 18:52 编辑
不会咧 @夏季的春秋 你来看,我不会C++{:10_256:} c++没学过,不会捏 在C++中,派生类对象的布局被编译器决定,因此在不同的编译器、操作系统及计算机架构下,表现可能不同。
对于给定的代码片段,由于动态分配内存的方式是使用指向派生类对象的基类指针,因此在访问 pb.data、pb.data 和 pb.data 时,只会访问到对象中第一个 Base 类成员变量 data。而这些对象在内存中的布局将取决于特定的编译器实现,所以无法确切预测这些数据成员的值。
但是可以确定的是,因为 Derived 类型对象的内存空间大于 Base 类型对象的内存空间,在向一个指向 Base 类型的指针赋值为指向 Derived 对象的指针时,最后一块空闲区域可能没有被完全填充(或者说未被正确地重新初始化)。因此,如果将 pb 转换为 Derived* 指针,则 ((static_cast<Derived *>(pb)) + 2)->data1 的值可能会受到初始值的影响,而这个值也没有办法精确预测。
页:
[1]