【问题标题】:When Are Destructors Called?什么时候调用析构函数?
【发布时间】:2019-04-07 16:50:10
【问题描述】:

我正在尝试创建一个函数来复制具有指针成员的类,因此指针指向的是副本而不是原始的。有一个特殊的例子,我不希望更改延续到原始版本。

在应该删除BoardState 对象之前调用析构函数时会出现问题。因此,当再次调用Copy 函数时,它会尝试删除指针,但不能因为它们已经被删除。

奇怪的是,如果我删除析构函数,一切正常。所以我认为如果对象被销毁,但指针没有被删除,那么分配的内存将与指针分离并导致内存泄漏。然而,事实并非如此。指针仍然保持它们的值。所以对我来说,似乎在没有删除对象的情况下调用了析构函数。

我知道我可以使用智能指针而不用担心使用析构函数,但我想从中获得学习经验。所以我希望有人能告诉我发生了什么。

复制功能:

void BoardState::Copy(BoardState state)
{
    copy = true;

    if (p1 != nullptr) {
        delete p1;
    }
    p1 = new Pawn(state.getP1());

    if (p2 != nullptr) {
        delete p2;
    }
    p2 = new Pawn(state.getP2());

    if (walls != nullptr) {
        delete walls;
    }
    walls = new list<Wall>(state.getWalls());
}

析构函数:

BoardState::~BoardState()
{
    if (copy) {
        if (p1 != nullptr) {
            delete p1;
        }
        if (p2 != nullptr) {
            delete p2;
        }
        if (walls != nullptr) {
            delete walls;
        }
    }
}

在这个函数的最后,SimulatedBoard 的析构函数被调用:

bool AI::startThinking(Pawn& pawn, Board& board)
{
    simulatedBoard.Copy(board.getBoardState());
    allPossibleDecision.clear();
    plan.clear();
    WallOptions.clear();

    if (!thinking) {
        thinking = true;
    }

    PathFinder pf(simulatedBoard);
    list<sf::Vector2f> path = pf.createPath(board.getPawn(m_turnPos)->getPawn().getPosition(), sf::Vector2f(0, m_goal));

    int opponent_turnPos = 1;
    if (m_turnPos == 1) {
        opponent_turnPos = 2;
    }

    int opponent_goal = 160;
    if (m_goal == 160) {
        opponent_goal = 640;
    }
    list<sf::Vector2f> opponent_path = pf.createPath(board.getPawn(opponent_turnPos)->getPawn().getPosition(), sf::Vector2f(0, opponent_goal));

    int difference = opponent_path.size() - path.size();

    int i;
    if (difference < 0 && totalWalls > 0) {
        i = 1;
    }
    else {
        i = 2;
    }

    switch (i) {
    case 1: {
        list<decision>::iterator nextMove;
        Wall placeWall;
        bool foundBetterDifference = false;

        addWallOptions(sf::Vector2f(190, 190),
            simulatedBoard.getPawn(m_turnPos).getPawn().getPosition(),
            simulatedBoard.getPawn(opponent_turnPos).getPawn().getPosition());

        for (list<decision>::iterator it = allPossibleDecision.begin(); it != allPossibleDecision.end(); it++) {
            decision d = (*it);
            Wall w(d.wallPlacement, wallColor);
            if (d.rotateWall) {
                w.Rotate();
            }

            simulatedBoard.addWall(w);
            opponent_path = pf.createPath(board.getPawn(opponent_turnPos)->getPawn().getPosition(), sf::Vector2f(0, opponent_goal));
            path = pf.createPath(board.getPawn(m_turnPos)->getPawn().getPosition(), sf::Vector2f(0, m_goal));
            simulatedBoard.removeWall(w);

            int newDifference = opponent_path.size() - path.size();
            if (newDifference > difference) {
                foundBetterDifference = true;
                difference = newDifference;
                nextMove = it;
                placeWall = w;
            }
        }

        if (foundBetterDifference) {
            board.addWall(placeWall);
            plan.push_back(*nextMove);
            totalWalls--;
            break;
        }
    }
    case 2 : 
        decision d;
        d.movePawn = true;
        d.pawnPos = path.front();
        plan.push_back(d);
        board.getPawn(m_turnPos)->getPawn().setPosition(path.front());
    }

    return false;
}

SimulatedBoard 不在此函数中创建。它是班级的成员。即使该类超出范围并且这就是删除SimulatedBoard 的原因,下次该类再次在范围内时,构造函数应该为SimulatedBoard 运行并将指针设置回nullptr。除非我的理解是错误的,否则很有可能。

【问题讨论】:

  • 这并没有解决问题,但您不需要在删除之前测试nullptrdelete 适用于空指针。
  • BoardState而不是Copy函数编写一个真正的复制构造函数和赋值运算符。您很有可能在某处通过值传递或返回BoardState,或者复制BoardState。您的 BoardState 缺少用户定义的复制构造函数和赋值运算符。阅读rule of 3
  • 关于你的问题“什么时候调用析构函数”。当您的对象超出范围时,或者如果使用new / new[] 动态创建,则会执行对delete / delete[] 的相应调用。
  • 此外,由于代码使用的是 C++11 或更高版本,因此您也应该有用户定义的移动构造函数和移动赋值运算符。 5 规则。
  • 是的,5 的规则。对于 OP,如果您实现了提到的功能,并且没有错误地实现它们,那么您调用析构函数的问题可能会神奇地消失。

标签: c++ pointers constructor copy destructor


【解决方案1】:

我建议您为 BoardState 类定义一个正确的copy constructor,而不是使用 Copy 函数。

我想PathFinder pf(simulatedBoard)simulatedBoard 是按值传递给 PathFinder 的构造函数的。 pf(simulatedBoard) 的结果将使用模拟板副本的析构函数。

由于simulatedBoard 有copy = true,它的副本也会有这个标志,所以delete 将被调用p1p2walls 指针。 请注意,Copy 函数中也会发生同样的情况(析构函数将从 state 参数调用)。在为 BoardState 定义复制构造函数之前,您不应按值传递它,因为生成的副本将具有 `copy = true' 标志。

【讨论】:

    猜你喜欢
    • 2015-09-23
    • 1970-01-01
    • 2021-02-16
    • 2020-11-05
    • 2018-05-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-04
    相关资源
    最近更新 更多