c++ 无法访问向量〈vector〈?>>的元素

6rvt4ljy  于 2022-12-15  发布在  其他
关注(0)|答案(1)|浏览(159)

我有这样一个类,其中唯一的属性是一个向量〈vector〈?〉〉,如下所示:

struct Pixel {
    float r, g, b;
};

class Test {
private:
  vector<vector<struct Pixel>> pixels;
public:
  void read(std::string filename);
}

read方法首先从文件中读取数据并放入一个一维数组:

std::vector<pixel> v;

while(getline(file, line_pixels)) {
  if(line_pixels.size() > 0 && line_pixels.at(0) != '#') {
    std::stringstream ss(line_pixels);
    std::string value;
    while(getline(ss, value, ' ')) {
      pixel p;
      p.r = p.g = p.b = stoi(value);
      v.emplace_back(p);
    }
  }
}

之后,我将数据从1d矢量传输到2d矢量pixels。我尝试了以下两种方法:

int index = 0;
size_t h = stoi(height), w = stoi(width);
for(size_t i=0; i<h; i++) {
  std::vector<pixel> row;
  for(size_t j=0; j<w; j++)
    row.push_back(v[index++]);
  pixels.push_back(row);
}

使用这段代码,当我尝试访问像pixel[index]这样的像素元素时,我得到了一个分段错误。
我尝试过的另一个选择是:

size_t h = stoi(height), w = stoi(width);
pixels.resize(h);
for(size_t i=0; i<h; i++)
  pixels[i].resize(w);
for(size_t i=0; i<v.size(); i++) {
  int row = i / w;
  int col = i % w;
  pixels[row][col] = v[i];
}

使用这段代码,我在调整大小的行中得到了一个分段错误。
有人能告诉我正确的方法吗?

更新

完整代码分为这2个项目:https://github.com/klebermo/codec包含实现读取方法的Netpbm和Bitmap类。https://github.com/klebermo/game使用上一链接中的方法。

ccrfmcuu

ccrfmcuu1#

就像其他人说的,错误不在你发布的代码片段中,这就是为什么人们希望有一个最小的可复制的自包含代码示例,以缩小错误的范围。
实际错误(或其中一个实际错误)在此行中:

float * vertices = image.toArray().data();

以更扩展的形式写出这个表达式意味着:

float * vertices;
{
    std::vector<float> temp = image.toArray();
    vertices = temp.data();
} // temp gets destroyed here, since it goes out of scope.
// accessing vertices after this point is undefined behavior
// because the memory it points to was deleted by the 
// destructor of temp

这意味着在这一行之后访问vertices会导致未定义的行为,特别是写入它会经常损坏其他对象。
为了避免这种情况,你可以让向量和vertices保持相同的持续时间,另外,不要在一个由std::vector管理的指针上调用delete

std::vector<float> vertices_vec = image.toArray();
float *vertices = vertices_vec.data();

这样,只要vertices_vec没有超出范围,vertices就会一直指向有效数据。
编辑:
另外,我猜图像读入代码并不是在所有情况下都有边界检查,所以如果输入文件无效,你可能会遇到其他问题。所以你可以在那里添加一些带有边界检查的Assert/调试检查。
编辑2:
你违反了一个定义规则。
在一个标头(Netpbm/src/netpbm.hpp)中,您有

class Netpbm {
protected:
  std::vector<std::vector<pixel>> pixels;
public:
  virtual void read_file(std::string file_name) = 0;
  virtual void write_ascii_file(std::string file_name) = 0;
  virtual void write_binary_file(std::string file_name) = 0;

  int getHeight();
  int getWidth();
  std::vector<float> toArray();
};

而在另一个标头(Netpbm/include/netpbm.hpp)中,您有

class Netpbm {
public:
  virtual void read_file(std::string file_name) = 0;
  virtual void write_ascii_file(std::string file_name) = 0;
  virtual void write_binary_file(std::string file_name) = 0;

  int getHeight();
  int getWidth();
  std::vector<float> toArray();
};

这两个向量的大小不同,你编译的部分代码采用一种大小,而其他部分代码采用另一种大小,因此破坏了堆栈。如果使用的代码不知道向量应该在那里,那么生成的构造函数将永远不会初始化它,所以你访问的是一个你从未调用过构造函数的向量。

相关问题