【问题标题】:Assertion error when passing object by value -- it is my copy constructor?按值传递对象时出现断言错误——它是我的复制构造函数?
【发布时间】:2015-08-02 08:03:57
【问题描述】:

大家!

我刚写完一个使用动态内存分配的二维迷宫(类是一个名为“迷宫”的 ADT——多么原始)。我将迷宫传递给另一个名为“MazeSolver”的类的方法,它使用递归和回溯来解决迷宫。好消息是当我通过引用传递对象时,我的代码编译得非常好。我不知道是好是坏的消息是,如果我尝试按值将 Maze 传递给 MazeSolver,则会收到断言错误。

鉴于错误仅在我按值传递时发生,我只能假设它与我的复制构造函数有关。在继续之前,这里有一些关于代码的信息:

迷宫由正方形组成。每个正方形都由一个名为 SquareData 的结构表示。

      struct SquareData 
    {
        //data fields for the struct (NO POINTERS)
    }

我决定用一个 SquareData 指针向量来表示整个迷宫(这个向量在“迷宫”类的私有部分中)。

vector<SquareData*> squares;

我的析构函数的实现看起来像这样(最后一次调用引用 Player 类只是消除了我声明为该类的静态变量的悬空指针,我已经指向迷宫。我不认为这很重要考虑到这个问题,但毕竟我是 C++ 新手,你们中的一个人可能认为它可能是,所以我将它包含在“hmmms”中):

// clears maze of its contents
void Maze::clear() {
    int totalSquares = squares.size();
    for (int loopVar = 0; loopVar < totalSquares; loopVar++)
    {
        delete squares[loopVar]; // deallocate memory by deleting the square structure
        squares[loopVar] = nullptr; // eliminate dangling pointer
    } // vector takes care of itself
} // end clear

Maze::~Maze(){
    //clear the maze of contents (vector is full of pointers whose memory is on the heap)
    clear();
    //vector safe to deallocate itself now
    Player::setMaze(nullptr); // remove the pointer from player
}

我在 header 中声明了复制构造函数,如下所示:

/** Copy Constructor */
    Maze(const Maze& myMaze);

尝试实施:

/** copy constructor */
Maze::Maze(const Maze& myMaze){
    /** Initialize Constants */
    mazeLength = myMaze.mazeLength;
    mazeWidth = myMaze.mazeWidth;
    exitRow = myMaze.exitRow;
    exitCol = myMaze.exitCol;
    entRow = myMaze.entRow;
    entCol = myMaze.entCol;
    /** copy the vector of pointers*/
    for (int loopVar = 0; loopVar < myMaze.squares.size(); loopVar++)
    {
        squares.push_back(myMaze.squares[loopVar]);
    }
} // end copy constructor

以下是我试图了解问题所在的方法:

我为我的 Maze 类编写了这个矢量显示函数。

void Maze::vectorDisplay() const {
    for (int loopVar = 0; loopVar < squares.size(); loopVar++)
    {
        cout << "Vector Index: " << loopVar << endl;
        cout << "Pointer: " << squares[loopVar] << endl;
        cout << "Row: " << squares[loopVar]->Row << endl;
        cout << "Col: " << squares[loopVar]->Col << endl;
        cout << "State: " << squares[loopVar]->State << endl;
    }
} //end vectorDisplay

并且发现在驱动中执行以下操作时矢量显示正确:

    Maze myMazeObject(// parameters);
    myMazeObject.vectorDisplay();

并且会毫无怨言地产生输出。

但是现在如果我在传递值时尝试使用这样的代码:

Maze myMazeObject(// parameters);
MazeSolver myMazeSolver;
myMazeSolver.someMazeSolverMethod(myMazeObject); 

其中 someMazeSolverMethod 有行 myMazeObject.vectorDisplay();我得到一个断言错误,就像正在打印向量中的最后一个元素一样。

我想说这是我的错,我的复制构造函数是一个 p.o.s.如果有任何见解,请告诉我如何解决它以及我将来可以做什么!

感谢您抽出宝贵的时间阅读,如果您愿意,请务必回答!

-J

【问题讨论】:

  • 感谢您为我指明方向!复制赋值运算符对我来说是新闻,我可以看到它在不久的将来为我解决了很多问题。

标签: c++ vector copy-constructor dynamic-memory-allocation assertions


【解决方案1】:

这是你的问题。

    squares.push_back(myMaze.squares[loopVar]);

基本上每个迷宫都有一个充满相同指针的向量。当迷宫的一个副本超出范围时,它将删除所有指针。因此,另一个迷宫现在有一组无效指针。

几个解决方案。

  1. 不要使用指针。

除非你 SquareData 是多态的,否则似乎没有理由保留指针。

 std::vector<SquareData> squares;
  1. 如果您希望迷宫的每个副本都指向相同的方格。

然后使用共享指针。这将记录对每个 SquareData 的引用次数,因此只有在它们真正超出范围时才会删除它们。

 std::vector<std::shared_ptr<SquareData>> squares;
  1. 最没有吸引力(并且可能不需要)。

更改代码以实际将指针内容复制到新对象中。

squares.push_back(new SquareData(myMaze.squares[loopVar]));

【讨论】:

  • 关于多态特性的好处:我真的无法预见通过 Maze 类使用的任何特性。不过,据我自己所知,假设我使用共享指针。如果所有引用都指向堆上分配的相同内存,那么在原始的生命周期结束并且副本尝试释放相同的内存后我会收到错误吗?无论哪种方式,我想一些共享指针学习是有序的!谢谢!
  • @JoshuaMacvey:共享指针基本上对指针进行引用计数。一旦引用计数达到零,则删除该对象。因此,它可以安全地用于任何数量的其他对象。
  • @JoshuaMacvey:但是SquareData 不是多态的,为什么要付出动态内存分配的成本。只需在向量中使用普通对象即可。这就是你通常在 C++ 中看到的内容
【解决方案2】:

使用

    squares.push_back(myMaze.squares[loopVar]);

在复制构造函数中会导致下游出现问题。如果squares 的内容是对象而不是指针,那将是有效的。

现在有两个对象持有指针。两者都会尝试在同一个指针上调用delete,这很容易导致未定义的行为。

您可以通过以下方式解决问题:

  1. 使用vector 对象而不是vector 指针,或
  2. 从堆中创建新对象并将它们添加到新对象中。

    squares.push_back(new SquareData(*myMaze.squares[loopVar]));
    

【讨论】:

  • 这很有意义。我可以看到您已取消引用我的原始指针并在堆上创建了一个新对象。通过内存管理,我可以相信两个单独的析构函数也会存在,以便在生命结束时删除副本和原始析构函数?
  • @JoshuaMacvey,最后一句话可以更好地表达,但我认为您理解解决方案,答案是“是”。
猜你喜欢
  • 2017-08-18
  • 2012-01-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-20
相关资源
最近更新 更多