【问题标题】:Pushing a struct that contains an array into a vector [closed]将包含数组的结构推送到向量中[关闭]
【发布时间】:2016-08-19 01:27:55
【问题描述】:

我正在编写一个执行差分进化的程序。有一次,我将我的解决方案整理成一个解决方案档案,并遇到了这个问题。我的存档是一个结构向量:

std::vector<SingleSolution> archive;

typedef struct SingleSolution
{
    int *crop_area;
    int *envf;
    double nr_cost;
    int env_cost;
    int front;
    int feasible;
} SingleSolution;

当我进行排序并有一个解决方案想要放入我的存档时,我正在创建一个 SingleSolution 并将其推送到存档:

SingleSolution member;
member.crop_area = (int *)calloc(crops + 1, sizeof(int));
member.envf = (int *)calloc(M + 1, sizeof(int));
member.front = 1;
member.feasible = feasible[member1];
member.nr_cost = nr_costs[member1];
member.env_cost = env_costs[member1];
for (int n = 1; n <= crops; n++) member.crop_area[n] = solution_crop[member1][n];
for (int n = 1; n <= M; n++) member.envf[n] = solution_env[member1][n];

archive.push_back(member);
//free(member.crop_area);
//free(member.envf);

我在推入向量后释放了这些数组,因为我认为它复制了我推入的内容,但在打印存档时,我看到了明显是垃圾值的内容并意识到了原因。如您所见,我已将其注释掉。 所以我的问题是,那个 push_back 到底发生了什么?它推动了内存中数组开始的位置,然后我释放了那个空间?有什么办法可以克服这个问题,还是需要在主函数中创建空间并在调用排序函数时将其传递,然后在完成时释放它?

【问题讨论】:

  • 如果您使用的是 c++,则无需自己处理原始指针。事实上,它实际上很气馁,你应该立即停止它!
  • 一个指针的副本,是另一个指向同一个位置的指针。您应该将 crop_areaenvf 设为向量
  • 您已经知道如何使用std::vector 来存储您的SingleSolution 实例。它也非常适合存储crop_areaenvf 信息。不要为难自己。
  • 我意识到这是迂腐的,但是您创建的结构不包含数组,它包含指针。包含一个数组就像int foo[30];。请更正问题标题。此外,在惯用 C++ 中进行这项工作的方法是为您的结构定义析构函数和复制构造函数(可能还有赋值操作和移动构造函数等)(或使用像向量这样的容器来为您处理这一切)..

标签: c++ arrays vector struct


【解决方案1】:

当你这样做时:

archive.push_back(member);

您要求它使用复制构造函数复制 memberarchive 向量的末尾。您可以说“SingleSolution 中没有复制构造函数”,但实际上存在 - 编译器提供的复制构造函数,它会生成结构成员的 shallow 副本。这就是您的问题所在。您正在使用原始指针,并且默认的复制构造函数仅复制指针的 (它们指向的内存地址 - 您的缓冲区)。

当您尝试自己管理这些资源时,这是导致未定义行为或内存泄漏的好方法。当您释放缓冲区但仍有指向旧内存的指针时,您意识到这一点。

让我们尝试用 C++ 重写这段代码。

首先,您不需要typedef

其次,你的缓冲区可以替换为std::vector

struct SingleSolution
{
    std::vector<int> crop_area;
    std::vector<int> envf;
    double nr_cost;
    int env_cost;
    int front;
    int feasible;
};

要填充您的结构,无需担心内存分配/释放,您可以这样做:

SingleSolution member;

member.crop_area.resize(crops+1);
member.crop_area[0] = 0;
std::copy(&solution_crop[member1][1], &solution_crop[member1][crops+1], member.crop_area.begin()+1);

member.envf.resize(M+1);
member.envf[0] = 0;
std::copy(&solution_env[member1][1], &solution_env[member1][M+1], member.envf.begin()+1);

member.front = 1;
member.feasible = feasible[member1];
member.nr_cost = nr_costs[member1];
member.env_cost = env_costs[member1];

archive.push_back(member);

或者这个:

SingleSolution member;

member.crop_area.reserve(crops+1);
member.crop_area.push_back(0);
std::copy(&solution_crop[member1][1], &solution_crop[member1][crops+1], std::back_inserter(member.crop_area));
// or: std::copy_n(&solution_crop[member1][1], crops, std::back_inserter(member.crop_area));

member.envf.reserve(M+1);
member.envf.push_back(0);
std::copy(&solution_env[member1][1], &solution_env[member1][M+1], std::back_inserter(member.envf));
// or: std::copy_n(&solution_env[member1][1], M, std::back_inserter(member.envf));

member.front = 1;
member.feasible = feasible[member1];
member.nr_cost = nr_costs[member1];
member.env_cost = env_costs[member1];

archive.push_back(member);

【讨论】:

  • 你的 copy 写出越界
  • @M.M 真的吗?我认为&amp;solution_crop[member1][crop] 将等同于.end() 成员函数。而且,在我看来,OP 初始化其操作数的方式很尴尬。所以,我把它改成 0 直到裁剪 - 1。或者我真的不明白你的评论
  • 是的,使用基于 0 的索引更自然,这就是 vector 使用的。但是 OP 的代码改为使用基于 1 的索引。我调整了您的示例以使其与基于 1 的逻辑兼容,因此它不会破坏使用 crop/envf 数据的用户代码的其余部分。此外,无论如何,您并没有在copy()'ing 之前将vectors 分配给它们。
  • @RemyLebeau 谢谢
  • @Amadeus copy 写入已分配的空间,您正在写入空向量
【解决方案2】:

push_back()SingleSolution 结构的副本 添加到向量的末尾。

现在,该结构恰好包含一些指针。这很好,但push_back() 不在乎。它不知道这些指针指向什么。它们可能指向一些静态缓冲区。它们可能指向动态分配的缓冲区(这就是你的情况),或者它们可能是完全未初始化的指针。

无论情况如何,push_back() 都不在乎。它的工作是将给定的结构复制到向量的末尾,这就是它的作用。它会复制结构中的任何内容,它不会在副本中分配任何新内容。

如果您动态分配一些缓冲区,并将结构中的指针设置为指向这些缓冲区,然后将结构的副本添加到向量,然后释放这些缓冲区,则向量中的结构中的指针将指向已释放的缓冲区,指针将不再有效。

因此,当结构的任何实例仍指向这些缓冲区时,不要释放原始缓冲区。只有在您清除了vector 后才释放它们。或者,您可以在结构中添加一个复制构造函数来分配它自己的内存缓冲区副本,并添加一个析构函数来释放这些复制的缓冲区,然后您可以随时释放原始缓冲区。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-18
    • 2019-07-11
    • 2019-12-30
    相关资源
    最近更新 更多