初始化和维护结构的结构

时间:2011-07-31 01:40:57

标签: c++ pointers struct

我正在编写C ++代码来处理从实验室测量中填充的一组直方图。当我尝试更好地组织事情时,我遇到了问题,我认为我的问题来自错误处理指针和/或结构。

我原来的设计看起来像这样:

// the following are member variables
Histogram *MassHistograms[3];
Histogram *MomentumHistograms[3];
Histogram *PositionHistograms[3];

其中每个数组的元素0对应于一个实验室测量,每个元素的元素1对应于另一个,等等。我可以通过MassHistograms[0]或类似的方式访问各个直方图,这样就可以了。然而,组织对我来说似乎不对 - 如果我要执行新的测量,我必须为每个直方图阵列添加一个元素。相反,我想出了

struct Measurement {
    Histogram *MassHistogram;
    Histogram *MomentumHistogram;
    Histogram *PositionHistogram;
};

作为一个复杂的附加层,我还想根据对数据进行的处理来捆绑这些测量,所以我做了

struct MeasurementSet {
    Measurement SignalMeasurement;
    Measurement BackgroundMeasurement;
};

我认为这种安排更具逻辑性和可扩展性 - 但它不起作用;-)如果我有像

这样的代码
MeasurementSet ms;
Measurement m = ms.SignalMeasurement;
Histogram *h = m.MassHistogram;

然后尝试用h做一些事情,我遇到了分段错误。由于类比代码之前工作正常,我认为我没有正确处理代码中的结构。具体来说,结构是否需要以任何方式显式初始化? (Histogram是由其他人的库提供的,只是声明Histogram *SomeHistograms[4]足以在之前初始化它们。)

我很感激反馈。我对Python和Clojure非常熟悉,但我对C ++的有限知识并没有扩展到[看起来像]结构的护理和喂养的奥秘: - )


我最终做了什么

我将Measurement变成了一个成熟的课程:

class Measurement {
    Measurement() {
        MassHistogram = new Histogram();
        MomentumHistogram = new Histogram();
        PositionHistogram = new Histogram();
    };

    ~Measurement() {
        delete MassHistogram;
        delete MomentumHistogram;
        delete PositionHistogram;
    };

    Histogram *MassHistogram;
    Histogram *MomentumHistogram;
    Histogram *PositionHistogram;
}

(我调用的通用Histogram()构造函数工作正常。)我遇到的另一个问题是通过总是通过引用传递Measurement来解决的。否则,析构函数将在任何接收到Measurement的函数的末尾被调用,并且下一次尝试使用其中一个直方图会发生段错误。

谢谢大家的答案!

4 个答案:

答案 0 :(得分:1)

当你的struct包含指针时,你必须自己初始化该变量。 例

struct foo
{
    int *value;
};

foo bar;
// bar.value so far is not initialized and points to a random piece of data

bar.value = new int(0);
// bar.value now points to a int with the value 0

// remember, that you have to delete everything that you new'd, once your done with it:
delete bar.value;

struct foo { int *value; }; foo bar; // bar.value so far is not initialized and points to a random piece of data bar.value = new int(0); // bar.value now points to a int with the value 0 // remember, that you have to delete everything that you new'd, once your done with it: delete bar.value;

答案 1 :(得分:1)

您知道Measurement的定义没有为实际的Histogram分配内存吗?在您的代码中,m.MassHistogram是一个悬空(未初始化)指针,它不指向任何测量的Histogram,也不指向任何能够存储Histogram的内存。正如@Nari Rennlos刚刚发布的那样,您需要将其指向现有(或新分配的)Histogram

您的第三方图书馆的界面是什么样的?如果可能的话,你应该有一个Measurement包含3 Histogram s(而不是指向Histogram的3个指针)。这样,当您创建MeasurementMeasurementSet时,将为您创建相应的Histogram,同样适用于销毁。如果您仍需要指针,则可以使用&运算符:

struct Measurement2 {
    Histogram MassHistogram;
    Histogram MomentumHistogram;
    Histogram PositionHistogram;
};

MeasurementSet2 ms;
Histogram *h = &ms.SignalMeasurement.MassHistogram; //h valid as long as ms lives

另请注意,只要您不使用指针(或引用),就会按值复制和分配对象:

MeasurementSet ms;                    //6 uninitialized pointers to Histograms
Measurement m = ms.SignalMeasurement; //3 more pointers, values taken from first 3 above
Histogram *h = m.MassHistogram;       //one more pointer, same uninitialized value

虽然指针已初始化,但此时所有10个指针都指向实际的Histogram

如果您有实际成员而不是指针,情况会变得更糟:

MeasurementSet2 ms;                    //6 Histograms
Measurement2 m = ms.SignalMeasurement; //3 more Histograms, copies of first 3 above
Histogram h = m.MassHistogram;         //one more Histogram

h.firstPoint = 42;
m.MassHistogram.firstPoint = 43;
ms.SignalMeasurement.MassHistogram.firstPoint = 44;

...现在你有3个略微不同的质量信号直方图,2对相同的动量和位置信号直方图,以及三个背景直方图。

答案 2 :(得分:1)

您确定Histogram *SomeHistograms[4]初始化了数据吗?如何填充直方图结构?

这里的问题不是结构,而是绊倒你的指针。执行此操作时:MeasurementSet ms;它声明了MeasurementSet类型的“自动变量”。这意味着MeasurementSet的所有内存都已“分配”并准备就绪。反过来,MeasurementSet有两个类型为Measurement的变量,它们也是“已分配”和“准备就绪”。反过来,测量有3个直方图*类型的变量,它们也是“已分配”和“准备就绪”......但是等等! “直方图*”类型是“指针”。这意味着它是一个地址 - 描述实际内存位置的32位或64位(或任何位)值。就是这样。你应该指出一些东西 - 把东西放在那个位置。在它指向任何东西之前,它会在其中包含字面上的随机数据(或者数据为0,或者某些特殊的调试数据,或类似的东西) - 关键是如果你尝试用它做某事,你会得到分段错误,因为您可能会尝试读取程序不应该读取的部分数据。

在c ++中,struct几乎与一个类(在python中有类似的概念)完全相同,你通常会像这样分配一个:

m.MassHistogram = new Histogram();

......之后,直方图已准备就绪。但是,YMMV:你能自己分配吗?或者你可以从一些图书馆获得一个,也许从设备阅读等?此外,尽管你可以做我写的东西,但它并不一定“漂亮”。 c ++-ic解决方案是将分配放在构造函数中(如python中的 init )并在析构函数中删除。

答案 3 :(得分:1)

首先,要记住结构和类几乎完全相同。唯一的区别是struct默认情况下是struct public,默认情况下class成员是private。 但其余的都完全相同。

其次,仔细区分指针和对象。

如果我写

Histogram h;

将分配直方图数据的空间,并调用它的构造函数。 (构造是一个与类完全相同的方法,这里是Historgram())

如果我写

Histogram* h;

我正在声明一个32/64位的变量,它将用作指向内存的指针。它用随机值初始化。危险!

如果我写

Histogram* h = new Histogram();

将为一个Histogram的数据成员分配内存,并调用它的构造函数。内存中的地址将存储在“h”中。

如果我写

Histogram* copy = h;

我再次声明一个32/64位变量指向内存中与h

完全相同的地址

如果我写

Histogram* h = new Historgram;
Histogram* copy = h;
delete h;

发生以下情况

  1. 为直方图对象分配内存
  2. 将调用直方图的构造函数(即使你没有编写它,你的编译器也会生成一个)。
  3. h将包含此对象的内存地址
  4. delete运算符将调用Histogram的析构函数(即使你没有编写它,你的编译器也会生成一个)。
  5. 为Histogram对象分配的内存将被释放
  6. copy仍将包含用于分配对象的内存地址。但你不能使用它。它被称为“悬空指针”
  7. h的内容将是未定义的
  8. 简而言之:代码中的“n.MassHistogram”指的是内存中的随机区域。不要使用它。首先使用运算符“new”分配它,或者将其声明为“Histogram”(对象而不是指针)

    欢迎来到CPP:D