什么时候调用析构函数?

When Are Destructors Called?

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

在应删除 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 不是在此函数内创建的。它是 class 的成员。即使 class 超出范围,这就是删除 SimulatedBoard 的原因,下一次 class 再次进入范围时,构造函数应该 运行 for SimulatedBoard 并将指针设置回 nullptr。除非我的理解是错误的,否则很可能是这样。

我建议您为您的 BoardState class 定义一个合适的 copy constructor 而不是使用 Copy 函数。

我假设在 PathFinder pf(simulatedBoard) 行上,simulatedBoard 按值传递给 PathFinder 的构造函数。 pf(simulatedBoard) 的结果将使用 simulatedBoard 副本的析构函数。

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