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