在二维向量(C++)中引用元素

5

我有一个名为Spring的类,它是粒子系统中的一部分。构造函数如下:

Spring(Particle& _a, Particle& _b);

我有一个粒子的向量,并且我使用

Spring mySpring = Spring(myParticles.at(j),myParticles.at(j+1));

在循环中添加两个粒子之间的弹簧力。到目前为止一切都运行良好。 然而,我想使用一个二维粒子向量。即:

Spring mySpring = Spring(myParticles.at(i).at(j),myParticles.at(i).at(j+1));

我不明白这个粒子的引用。在第一个例子中,每当我更改我的弹簧类中的粒子时,向量中的粒子会被更改。在第二个例子中,更改只是本地的。如何更改2D向量中的粒子?

编辑: 我想澄清一些事情:

我有一些粒子系统,每个系统由若干粒子组成。每个粒子只能与属于自己的相同系统中的其他粒子互动。因此,我有一个粒子系统的向量,其中每个粒子系统都是粒子对象的向量。(这使得2D向量的作用)。第一维(i)是系统,第二维(j)是单个粒子。 系统中的粒子彼此互动(碰撞、避免等),它们的位置会发生变化。并且向量会“更新”。(即,引用有效)。

但是,我有一个第二个(1D)弹簧力的向量。弹簧力也用于更新粒子的位置。 我的构造函数执行以下操作:

Spring::Spring(Particle& _a, Particle& _b) {
    a=&_a;
    b=&_b; }

假设a和b都是Particle*类型,因此我将这两个粒子的指针存储在2D向量中。另一个名为Spring.doSpring()的函数会更改粒子的位置。

a->pos.x=300;

或者

a->velocity+=something..

在我发布的第一个示例中,我只使用了一个粒子系统,因此不需要2D向量。一切都很好。向量中的粒子被更新。 但是在第二个示例中,我的程序运行正常,但无论doSpring函数做什么,2D向量中的粒子都不会被更新。

你是如何声明“myParticles”的? - coelhudo
嘿,来自http://www.drdobbs.com/cpp/184401863的提示:“不要过度立法命名,但确保使用一致的命名约定:只有两个必须遵守的原则:a)永远不要使用“下划线名称”,即以下划线开头或包含双下划线的名称;”出自Herb Sutter和Andrei Alexandrescu之口。 - coelhudo
4个回答

6

在引用/指向向量内部元素时,最常见的问题之一是重新分配。例如,如果您使用push_back方法,可能会导致向量超出其容量,分配一个新的内存块,将所有内容复制过去,然后释放旧的内存块。如果您已经引用或指向向量内部的元素,则这些引用或指针仍然指向旧的内存块,现在是无效的内存,这是一个严重的错误!

所以我猜测你的粒子效果不断向粒子向量中添加新的粒子,这在某些情况下会导致向量超出容量而进行重新分配。然而Spring类存储的指针没有被更新,所以它们指向死亡的内存,并且对实际的粒子没有影响,因为向量已经将其移动到其他地方。

不要引用或指向向量内部的元素。使用指针向量、列表或其他不会重新排列实际元素内存地址的容器。如果必须引用或指向向量内部的元素,请使用迭代器。在调试版本中,假设您有一个经过检查的STL实现,如果在向量重新分配自身后通过迭代器访问元素,则会收到调试警报。


1
但是这样不会导致空指针吗?这会导致应用程序崩溃。因为我的应用程序没有崩溃,只是元素没有被更新。 不过,这是一个很好的答案,我没有考虑到重新分配。 - hrst
不,没有任何东西被设置为 null 指针:vector 不知道 Spring 类中的指针,因此从不试图更改它们。这些指针没有被修改或设置为 null,它们只是指向一个已死的内存位置。由于内存可能在进程内的某个地方,所以读取和写入是允许的,尽管您可能会破坏应用程序内存。 - AshleysBrain

5
你所做的看起来没问题 - 以下代码创建了一个“2D”向量,并说明了 .at().at() 结构确实会给你一个引用:
#include <vector>
#include <iostream>
using namespace std;

int main() {
    vector <vector<int> > vi;
    vi.push_back( vector <int>() );
    vi.at(0).push_back( 42 );
    cout << vi.at(0).at(0) << endl;    // prints 42
    vi.at(0).at(0) = 666;
    cout << vi.at(0).at(0) << endl;    // prints 666
}

1

我认为C++ FAQ Lite中的这个系列应该会有所帮助。

请不要被“运算符重载”标题所迷惑。你一定要阅读13.10、13.11和13.12。


0

如果我理解正确,您想使用std::vector创建一个粒子的二维数组?

如果是这样,您可以将其声明为:std::vector<std::vector<Particle> >。然后,您甚至可以使用[][]符号来访问元素。(危险!在使用此运算符时没有边界检查

但是,如果这个二维数组大部分都是零,那么使用一个以索引为键的map可能会更好。


从他的代码(.at(i).at(j))来看,他已经在使用向量的向量。 - Max Shawabkeh
从上面的例子来看,这似乎正是他已经尝试做的(即“at(...).at(...)”部分)。 - rjnilsson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接