【问题标题】:Add addresses of Objects to a vector in loop将对象的地址添加到循环中的向量
【发布时间】:2016-08-12 10:25:31
【问题描述】:

我需要创建几个对象并将它们放在一个列表中(为此我使用 std::vector)。此外,我需要列表项指向对象的地址,以便我对对象所做的更改也反映在列表中。 但问题是,列表中的每个项目都指向循环中创建的最后一个对象。

    for(int i=0;i<50;i++){
        for(int j=0;j<50;j++){
            Grass g1;
            g1.position.x = i;
            g1.position.y = j;
            grassList.push_back(&g1);
        }
    }

列表中草对象的属性应该是..

[0,0]
[0,1]
[0,2]
.
.
.
.
[49,49]

但事实证明……

[49,49]
[49,49]
[49,49]
[49,49]
.
.
.
[49,49]

【问题讨论】:

  • 要么按值推送,要么创建一个新的 Grass 对象(如使用“new”运算符)。 g1 是在堆栈上创建的,基本上是您一遍又一遍地使用其地址的同一个对象。

标签: c++ loops pointers


【解决方案1】:

您正在将指向 局部变量 的指针推送到向量。局部变量在其作用域结束时被销毁(在本例中为倒数第二个 })。因此,在} 之后取消引用这些指针中的任何一个都是未定义的行为。您看到的输出是未定义行为的完全有效结果。

在这里使用指针是没有意义的。仅在绝对必要时使用它们(包括new)。欲了解更多信息,请参阅Why should I use a pointer rather than the object itself?

这是正确的方法:

std::vector<Grass> grassList;
         // ^^^^^ not pointers!

for(int i=0; i<50; ++i) {
    for(int j=0; j<50; ++j) {
        Grass g1;
        g1.position.x = i;
        g1.position.y = j;
        grassList.push_back(g1);
    }
}

【讨论】:

  • 在这里使用指针没有意义”可能过于绝对。在大多数情况下,使用对象向量可能效果很好,但 OP 可能 的特定需求也可能不会被涵盖。链接问题中接受的答案更加谨慎:“您应该只在需要时使用动态存储持续时间。
  • 一开始我没有使用指针。我做的和你上面写的完全一样。但问题是,当我在代码的其余部分(向量列表之外)中对这些对象进行更改时,它们并没有反映在列表中(出于显而易见的原因)。也许我应该直接从列表本身引用对象。谢谢你说清楚。
【解决方案2】:

如果您习惯于使用变量引用计数的其他语言,您可能已经预料到了

Grass g1;

在循环的每次迭代中都创建了一个新的“草”对象。不是,C++ 不是引用计数语言。

相反,它在堆栈上创建了一个作用域局部变量。

因为您是在循环中执行此操作,所以它可能每次都位于内存中的同一位置。

您将需要:

  1. 不用指针,只需让你的容器成为 Grass 对象的容器:让容器为你处理分配。

  2. 使用 C++11 的 unique_ptr 和 C++14 的 make_unique 为循环的每次迭代动态创建 Grass 实例。当包含 unique_ptrs 的向量超出范围时,它们将被自动释放。

  3. 使用newdelete关键字手动分配和释放Grass对象指向。

选项 1:

#include <vector>

struct Grass {
    struct {
        int x, y;
    } position;
};

int main() {
    std::vector<Grass> grassList;
    for(int i=0;i<50;i++){
        for(int j=0;j<50;j++){
            Grass g1;
            g1.position.x = i;
            g1.position.y = j;
            grassList.push_back(g1);
        }
    }  
}

现场演示:http://ideone.com/DQs3VA

选项 2:

#include <memory>
#include <vector>

struct Grass {
    struct {
        int x, y;
    } position;
};

int main() {
    std::vector<std::unique_ptr<Grass>> grassList;
    for(int i=0;i<50;i++){
        for(int j=0;j<50;j++){
            auto g1 = std::make_unique<Grass>();
            g1->position.x = i;
            g1->position.y = j;
            grassList.push_back(std::move(g1));
        }
    }   
}

现场演示:http://ideone.com/hJUdwR

选项 3:

#include <vector>

struct Grass {
    struct {
        int x, y;
    } position;
};

int main() {
    std::vector<Grass*> grassList;
    for(int i=0;i<50;i++){
        for(int j=0;j<50;j++){
            Grass* g1 = new Grass;
            g1->position.x = i;
            g1->position.y = j;
            grassList.push_back(g1);
        }
    }
    // ...
    for (auto& g1: grassList) {
        delete g1;
    }
    grassList.clear();
}

现场演示:http://ideone.com/GTk7ON

【讨论】:

  • std::list?真的吗? — Why you should avoid Linked Lists.
  • @tuple_cat 根据他的命名猜测他使用的容器。不是我选择的容器。
  • 知道这不是您选择的容器,我松了一口气,但我想知道您为什么在示例中使用它。此外,引用 OP:“我正在使用 std::vector”
  • @tuple_cat 错过了,更改了类型。
  • 非常感谢。你解释得很清楚。
【解决方案3】:

如果出于任何(未指定)原因需要存储指向对象位置的指针,则需要此

std::vector<Grass*> grassList;  // Assumed you already have something like this
for(int i=0;i<50;i++){
    for(int j=0;j<50;j++){
        Grass* gp1 = new Grass;
        gp1->position.x = i;
        gp1->position.y = j;
        grassList.push_back(gp1);
    }
}

这避免了您存储的指针指向的对象的破坏。参见,例如,Creating an object: with or without `new` 您必须注意适当地删除带有delete 的对象以释放内存。

但根据您的需要,您也可以使用对象数组而不是指针数组,如 tuple_cat 所建议的那样。

【讨论】:

    猜你喜欢
    • 2020-12-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-19
    • 2016-03-17
    • 2014-05-20
    • 2015-11-28
    相关资源
    最近更新 更多