【问题标题】:Segmentation fault when using vectors in the class and constructor在类和构造函数中使用向量时出现分段错误
【发布时间】:2020-03-22 00:23:05
【问题描述】:

我正在做一个编程项目的列表,而这个项目是做一个 15 拼图(幻灯片拼图)。当我遇到一个小障碍时,我正在做这个项目。

我的代码编译得很好,但是当我运行它时,我在第 12 行遇到了分段错误:pos[0] = x;

#include <iostream>
#include <vector>
#include <stdlib.h>
#include <time.h>
using namespace std;
class Tile{
private:
    vector<int> pos;
    int value;
public:
    Tile(int x, int y, int value_){
        pos[0] = x;
        pos[1] = y;
        value = value_;
    }
    ~Tile(){}
    int getPos(int a){return pos[a];}
    void setPos(int a, int b){pos[a] = b;}
};
int main(){
    Tile tile1(1, 2, 10);
    Tile* t1;
    t1 = &tile1;

    // returns position "x"
    cout << t1->getPos(0);
    return 0;
}

我的意思是,我可以直接完成整个项目,而不必使用向量/数组来处理位置,但我仍然想知道,为了我自己将来的理解,为什么这不起作用。

根据我运行的调试,程序在初始化 pos[] 向量的值时遇到问题。

另一个问题:可能相关,我尝试在实例化向量时设置它的大小。

vector&lt;int&gt; pos(2);

然后我得到调试错误:

error: expected identifier before numeric constant

不确定这里发生了什么。我尝试了很多不同的方法,但我似乎无法弄清楚为什么我的向量在类中不起作用。

我确信有一百种方法可以让我把这件小事做得更好,我很想知道你会如何解决它,但我也需要知道哪里出了问题,特别是在我写过也试过了。

谢谢。

【问题讨论】:

  • pos[0] = x; 是未定义的行为。
  • 为什么要使用向量,它总是2个坐标,使用数组。

标签: c++ pointers vector segmentation-fault


【解决方案1】:

我尝试在实例化向量时设置它的大小。

vector<int> pos(2);

然后我得到调试错误:

error: expected identifier before numeric constant

这是编译错误,不是调试错误。

你不能这样初始化成员。但是,您可以(并且应该)使用父构造函数初始化它们:

Tile(int x, int y, int value_)
    : pos(2)
{
    pos[0] = x;
    pos[1] = y;
    value = value_;
}

目前,您只是将向量留空,然后访问(并写入!)不存在的元素。

无论如何,你真的不想要一个向量:这是很多动态分配。一个漂亮的数组怎么样?或者只是两个ints。

【讨论】:

    【解决方案2】:

    正如其他答案中提到的,您的向量是空的,您的代码正在尝试分配不存在的元素。

    解决方案是总是使用初始化器而不是赋值。重写你的构造函数如下:

    Tile(int x, int y, int value) :
        pos{x, y},
        value{value} {}
    

    请注意,构造函数主体现在是。所有初始化都发生在它应该发生的地方——在初始化列表中。

    除此之外,您的类不需要显式定义的析构函数;默认析构函数工作正常。

    这个类还有其他问题——例如,当用户执行tile.setPos(3, 4) 时会发生什么?良好 API 设计的经验法则是防止滥用 API。

    我会这样写你的 Tile 类:

    struct Tile {
        int x;
        int y;
        int value;
    
        Tile(int x, int y, int value) : x{x}, y{y}, value{value} {}
    };
    

    在您的案例中,getter 和 setter 并没有真正做任何有意义的工作。有一个论据可以将所有数据成员隐藏在访问器后面,以实现面向未来的访问控制。我不再相信这实际上有用,但以防万一,这里也有一个解决方案:

    class Tile {
        int x_;
        int y_;
        int value_;
    
    public:
        Tile(int x, int y, int value) : x_{x}, y_{y}, value_{value} {}
    
        int x() const { return x; }
        int& x() { return x; }
    
        int y() const { return y; }
        int& y() { return y; }
    
        int value() const { return value; }
    };
    

    这使得xy 可读可写(通过赋值:t.x() = 42;),而value 仅可读。其他 API 也是可能的,但需要进行不同的权衡。重要的是要保持一致。

    【讨论】:

    • 这是创建一个带有x 元素初始化为y 的向量还是一个包含两个元素xy 的向量?我永远记不得了……
    • @foreknownas_463035818 但很明显! “统一”初始化,对吧?
    • “有一个论点可以将所有数据成员隐藏在访问者后面,以实现面向未来的访问控制。” 就个人而言,我赞同 thin getters are (if perhaps subtly) functioning contrary to that goal 的学派.
    • @LightnessRaceswithMonica 是的,我不想讲太多细节,但我得出了相同的结论。
    • @KonradRudolph 酷 :)
    【解决方案3】:

    您的构造函数没有设置大小,因此当您尝试访问/修改其内容时,您可能会遇到异常。

    Tile(int x, int y, int value_) : pos(2) {
        pos[0] = x;
        pos[1] = y;
        value = value_;
    }
    

    您可以使用构造函数的初始化列表来调用vector的构造函数,如上面的代码。

    【讨论】:

      【解决方案4】:

      给定代码中有几个问题,我已经解决并在代码中添加了注释。

      setPosgetPos 中的问题可能会引发必须处理的分段错误。

      添加了相同的检查。

      #include <iostream>
      #include <vector>
      #include <stdlib.h>
      #include <time.h>
      using namespace std;
      class Tile{
      private:
          vector<int> pos;
          int value;
      public:
      
          Tile(int x, int y, int value_){
              pos.push_back(x); // this is equivalent to pos[0] = x, in this case 
              pos.push_back(y); // this is equivalent to pos[0] = y, in this case
              value = value_;
          }
      
          ~Tile(){}
      
          int getPos(int a){
              if(a >= pos.size()){
                  return -1; // if a is greater than size then pos[a] will raise the segmentation fault
              }
              return pos[a];
          }
          void setPos(int a, int b){
              if(a >= pos.size()){
                  pos.resize(a+1); // to avoid segmentation fault, we are increasing the size if the given index is higher
                  // resize initialise the value with 0 as default value.
              }
              pos[a] = b;
          }
      };
      int main(){
          Tile tile1(1, 2, 10);
          Tile* t1;
          t1 = &tile1;
      
          // returns position "x"
          cout << t1->getPos(0);
          return 0;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-09-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-04-06
        • 1970-01-01
        相关资源
        最近更新 更多