简体   繁体   English

初始化和维护结构的结构

[英]Initializing and maintaining structs of structs

I'm writing C++ code to deal with a bunch of histograms that are populated from laboratory measurements. 我正在编写C ++代码来处理从实验室测量中填充的一堆直方图。 I'm running into problems when I try to organize things better, and I think my problems come from mishandling pointers and/or structs. 当我尝试更好地组织事情时,我遇到了问题,我认为我的问题来自错误处理指针和/或结构。

My original design looked something like this: 我原来的设计看起来像这样:

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

where element 0 of each array corresponded to one laboratory measurement, element 1 of each corresponded to another, etc. I could access the individual histograms via MassHistograms[0] or similar, and that worked okay. 其中每个阵列的元素0对应于一个实验室测量,每个元素的元素1对应于另一个,等等。我可以通过MassHistograms[0]或类似的方式访问各个直方图,并且工作正常。 However, the organization didn't seem right to me—if I were to perform a new measurement, I'd have to add an element to each of the histogram arrays. 然而,组织对我来说似乎不对 - 如果我要执行新的测量,我必须为每个直方图阵列添加一个元素。 Instead, I came up with 相反,我提出了

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

As an added layer of complexity, I further wanted to bundle these measurements according to the processing that has been done on their data, so I made 作为一个复杂的附加层,我还想根据对其数据进行的处理来捆绑这些测量,所以我做了

struct MeasurementSet {
    Measurement SignalMeasurement;
    Measurement BackgroundMeasurement;
};

I think this arrangement is much more logical and extensible—but it doesn't work ;-) If I have code like 我认为这种安排更具逻辑性和可扩展性 - 但它不起作用;-)如果我有类似的代码

MeasurementSet ms;
Measurement m = ms.SignalMeasurement;
Histogram *h = m.MassHistogram;

and then try to do stuff with h , I get a segmentation fault. 然后尝试用h做的东西,我得到一个分段错误。 Since the analogous code was working fine before, I assume that I'm not properly handling the structs in my code. 由于类比代码之前工作正常,我认为我没有正确处理代码中的结构。 Specifically, do structs need to be initialized explicitly in any way? 具体来说,结构是否需要以任何方式显式初始化? (The Histogram s are provided by someone else's library, and just declaring Histogram *SomeHistograms[4] sufficed to initialize them before.) Histogram由其他人的库提供,只是声明Histogram *SomeHistograms[4]足以在之前初始化它们。)

I appreciate the feedback. 我很感激反馈。 I'm decently familar with Python and Clojure, but my limited knowledge of C++ doesn't extend to [what seems like] the arcana of the care and feeding of structs :-) 我对Python和Clojure非常熟悉,但我对C ++的有限知识并没有扩展到[看起来像]结构的关注和喂养的奥秘:-)


What I ended up doing 我最终做了什么

I turned Measurement into a full-blown class: 我将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;
}

(The generic Histogram() constructor I call works fine.) The other problem I was having was solved by always passing Measurement s by reference; (我调用的通用Histogram()构造函数工作正常。)我遇到的另一个问题是通过始终通过引用传递Measurement来解决的; otherwise, the destructor would be called at the end of any function that received a Measurement and the next attempt to do something with one of the histograms would segfault. 否则,析构函数将在接收到Measurement的任何函数的末尾被调用,并且下一次尝试使用其中一个直方图进行某些操作会发生段错误。

Thank you all for your answers! 谢谢大家的答案!

When your struct contains a pointer, you have to initialize that variable yourself. 当你的struct包含一个指针时,你必须自己初始化该变量。 Example

 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; 

You are aware that your definition of Measurement does not allocate memory for actual Histogram s? 您知道您对Measurement的定义不会为实际Histogram分配内存吗? In your code, m.MassHistogram is a dangling (uninitialized) pointer, it's not pointing to any measured Histogram , nor to any memory capable of storing a Histogram . 在你的代码中, m.MassHistogram是一个悬空(未初始化)指针,它不指向任何测量的Histogram ,也不指向任何能够存储Histogram存储器。 As @Nari Rennlos posted just now, you need to point it to an existing (or newly allocated) Histogram . 正如@Nari Rennlos刚刚发布的那样,您需要将其指向现有(或新分配的) Histogram

What does your 3rd party library's interface look like? 你的第三方图书馆的界面是什么样的? If it's at all possible, you should have a Measurement containing 3 Histogram s (as opposed to 3 pointers to Histogram s). 如果可能的话,你应该有一个包含3个HistogramMeasurement (而不是Histogram的3个指针)。 That way when you create a Measurement or a MeasurementSet the corresponding Histogram s will be created for you, and the same goes for destruction. 这样,当您创建MeasurementMeasurementSet时,将为您创建相应的Histogram ,同样适用于破坏。 If you still need a pointer, you can use the & operator: 如果仍需要指针,可以使用&运算符:

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

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

Also note that as long as you're not working with pointers (or references), objects will be copied and assigned by value: 另请注意,只要您不使用指针(或引用),就会按值复制和分配对象:

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

Though if the pointers had been initialized, all 10 of them would be pointing to an actual Histogram at this point. 虽然如果指针已经初始化,那么它们中的所有10个指向此时的实际Histogram

It gets worse if you have actual members instead of pointers: 如果您有实际成员而不是指针,情况会变得更糟:

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;

...now you have 3 slightly different mass signal histograms, 2 pairs of identical momentum and position signal histograms, and a triplet of background histograms. ...现在你有3个略微不同的质量信号直方图,2对相同的动量和位置信号直方图,以及三个背景直方图。

Are you sure that Histogram *SomeHistograms[4] initialized the data? 你确定Histogram *SomeHistograms[4]初始化了数据吗? How do you populate the Histogram structs? 如何填充直方图结构?

The problem here is not the structs so much as the pointers that are tripping you up. 这里的问题不是结构,而是绊倒你的指针。 When you do this: MeasurementSet ms; 执行此操作时: MeasurementSet ms; it declares an 'automatic variable' of type MeasurementSet. 它声明了MeasurementSet类型的“自动变量”。 What it means is that all the memory for MeasurementSet is 'allocated' and ready to go. 这意味着MeasurementSet的所有内存都已“分配”并准备就绪。 MeasurementSet, in turn, has two variables of type Measurement that are also 'allocated' and 'ready to go'. 反过来,MeasurementSet有两个类型为Measurement的变量,它们也是“已分配”和“准备就绪”。 Measurement, in turn, has 3 variables of type Histogram * that are also 'allocated' and 'ready to go'... but wait! 反过来,测量有3个直方图*类型的变量,它们也是“已分配”和“准备就绪”......但是等等! The type 'Histogram *' is a 'pointer'. “直方图*”类型是“指针”。 That means it's an address - a 32 or 64 bit (or whatever bit) value that describes an actual memory location. 这意味着它是一个地址 - 描述实际内存位置的32位或64位(或任何位)值。 And that's it. 就是这样。 It's up to you to make it point to something - to put something at that location. 你应该指出一些东西 - 把东西放在那个位置。 Before it points to anything, it will have literally random data in it (or 0'd out data, or some special debug data, or something like that) - the point is that if you try to do something with it, you'll get a segmentation fault, because you will likely be attempting to read a part of data your program isn't supposed to be reading. 在它指向任何东西之前,它会在其中包含字面上的随机数据(或者数据为0,或者某些特殊的调试数据,或类似的东西) - 关键是如果你尝试用它做某事,你会得到分段错误,因为您可能会尝试读取您的程序不应该读取的数据的一部分。

In c++, a struct is almost exactly the same thing as a class (which has a similar concept in python), and you typically allocate one like so: 在c ++中,struct几乎与一个类(在python中有类似的概念)完全相同,你通常会像这样分配一个:

m.MassHistogram = new Histogram();

...after that, the histogram is ready-to-go. ......之后,直方图即可开始使用。 However, YMMV: can you allocate one yourself? 但是,YMMV:你能自己分配吗? Or can you only get one from some library, maybe from a device reading, etc? 或者你可以从一些图书馆获得一个,也许从设备阅读等? Furthermore, although you can do what I wrote, it's not necessarily 'pretty'. 此外,尽管你可以做我写的东西,但它并不一定“漂亮”。 A c++-ic solution would be to put the allocation in a constructor (like init in python) and delete in a destructor. 一个c ++-ic解决方案是将分配放在构造函数中(比如python中的init )并在析构函数中删除。

First, always remember that structs and classes are almost exactly the same things. 首先,要记住结构和类几乎完全相同。 The only difference is that struct members are public by default, and a class member is private by default. 唯一的区别是struct默认情况下是struct public,默认情况下class成员是private。 But all the rest is exactly the same. 但其余的都是完全一样的。

Second, carefully differentiate between pointers and objects. 其次,仔细区分指针和对象。

If I write 如果我写

Histogram h;

space for histogram's data will be allocated, and it's constructor will be called. 将分配直方图数据的空间,并调用它的构造函数。 ( A construct is a method with exactly the same name as the class, here Historgram() ) (构造是一个与类名完全相同的方法,这里是Historgram())

If I write 如果我写

Histogram* h;

I'm declaring a variable of 32/64 bits that will be used as a pointer to memory. 我正在声明一个32/64位的变量,它将用作指向内存的指针。 It's initialzed with a random value. 它用随机值初始化。 Dangerous! 危险!

If I write 如果我写

Histogram* h = new Histogram();

memory will be allocated for one Histogram's data members, and it's constructor will be called. 将为一个Histogram的数据成员分配内存,并调用它的构造函数。 The address in memory will be stored in "h". 内存中的地址将存储在“h”中。

If I write 如果我写

Histogram* copy = h;

I'm again declaring a 32/64 bit variable that points to exactly the same address in memory as h 我再次声明一个32/64位变量,指向内存中与h完全相同的地址

If I write 如果我写

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

the following happens 发生以下情况

  1. memory is allocated for a Histogram object 为直方图对象分配内存
  2. The constructor of Histogram will be called (even if you didn't write it, your compiler will generate one). 将调用直方图的构造函数(即使您没有编写它,您的编译器也会生成一个)。
  3. h will contain the memory address of this object h将包含此对象的内存地址
  4. the delete operator will call the destructor of Histogram (even if you didn't write it, your compiler will generate one). delete运算符将调用Histogram的析构函数(即使你没有编写它,你的编译器也会生成一个)。
  5. the memory allocated for the Histogram object will be deallocated 为Histogram对象分配的内存将被释放
  6. copy will still contain the memory address where the object used to be allocated. copy仍将包含用于分配对象的内存地址。 But you're not allowed to use it. 但你不能使用它。 It's called a "dangling pointer" 它被称为“悬空指针”
  7. h's contents will be undefined h的内容将是未定义的

In short: the "n.MassHistogram" in your code is referring to a random area in memory. 简而言之:代码中的“n.MassHistogram”指的是内存中的随机区域。 Don't use it. 不要使用它。 Either allocated it first using operator "new", or declare it as "Histogram" (object instead of pointer) 首先使用运算符“new”分配它,或者将其声明为“Histogram”(对象而不是指针)

Welcome to CPP :D 欢迎来到CPP:D

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM