shared_ptr 递归函数中的赋值导致分段错误
shared_ptr assignment in recursive function causing Segmentation Fault
为发布这么多代码提前致歉...
我正在构建一个名为 Chickenfoot 的类似多米诺骨牌游戏的模拟,在该游戏中,玩家从墓地抽 "Bones" 到他们手中,然后在场地上玩多米诺骨牌。
这是我尝试使用智能指针的第一个程序,我 运行 遇到了一个我似乎无法查明原因的问题。偶尔 运行 宁这个程序给我一个分段错误。下面可以看到 gdb 堆栈跟踪。
这个答案表明这可能与它是一个递归函数有关。
我在这里做错了什么?此外,如果我滥用了这些 shared_ptr 实例中的任何一个或可以改进实现,任何建议将不胜感激 - 谢谢!
ChickenFoot.cpp
#include <iostream>
#include <cstdlib>
#include <ctime>
#include "Game.h"
const int NUM_OF_PLAYERS = 4;
int main(int argc, char** argv) {
std::srand(std::time(0));
Game* chickenfoot = new Game(NUM_OF_PLAYERS);
chickenfoot->start(DOMINOES_SET_SIZE);
delete chickenfoot;
return 0;
}
Game.h
#include <memory>
#include <vector>
#include "Boneyard.h"
#include "Player.h"
#include "Field.h"
const int INITIAL_HAND_SIZE = 7;
static const int DOMINOES_SET_SIZE = 9;
const bool DEBUG = false;
class Game {
private:
std::vector< std::shared_ptr<Player> > players;
std::shared_ptr<Boneyard> boneyard;
bool played_rounds[DOMINOES_SET_SIZE]; // This will keep track of which double rounds have already been played
int getHighestUnplayedRound(bool* played);
int getNextHighestUnplayedRound(bool* played, int round);
public:
Game(int num_of_players);
void start(int highest_double);
};
Game.cpp
#include "Game.h"
#include <iostream>
Game::Game(int num_of_players)
{
boneyard = std::make_shared<Boneyard>();
for (int i = 0; i < num_of_players; i++) {
players.emplace_back(std::make_shared<Player>(i));
}
for (int i = 0; i <= DOMINOES_SET_SIZE; i++) {
played_rounds[i] = false;
}
}
void Game::start(int highest_double)
{
if (highest_double < 0) {
return;
} else {
boneyard->initialize();
for (int i = 0; i < INITIAL_HAND_SIZE; i++) {
for (std::vector< std::shared_ptr<Player> >::iterator j = players.begin(); j != players.end(); j++) {
(*j)->draw(boneyard);
}
}
for (std::vector< std::shared_ptr<Player> >::iterator i = players.begin(); i != players.end(); i++) {
if ((*i)->hasDouble(highest_double)) {
std::shared_ptr<Bone> hd_bone = (*i)->getDouble(highest_double);
// Do something here to actually play the game...
played_rounds[highest_double] = true;
break;
}
}
}
for (std::vector< std::shared_ptr<Player> >::iterator i = players.begin(); i != players.end(); i++) {
(*i)->discardAll();
}
if (played_rounds[highest_double]) {
start(getHighestUnplayedRound(played_rounds));
} else {
start(getNextHighestUnplayedRound(played_rounds, highest_double));
}
}
Player.h
#include "Bone.h"
#include "Boneyard.h"
#include <vector>
#include <memory>
class Player {
private:
int id;
std::vector< std::shared_ptr<Bone> > hand;
struct isDouble {
int m_value;
isDouble(int value) : m_value(value) {}
bool operator()(const std::shared_ptr<Bone> b) const {
return (b->getLeft() == m_value && b->isDouble());
}
};
public:
Player(int id);
void draw(std::shared_ptr<Boneyard> yard);
std::shared_ptr<Bone> getDouble(int number);
bool hasDouble(int number);
void discardAll();
};
Player.cpp
#include <iostream>
#include <algorithm>
#include "Player.h"
...
std::shared_ptr<Bone> Player::getDouble(int number)
{
auto result = std::find_if(hand.begin(), hand.end(), isDouble(number));
if (result != hand.end()) {
hand.erase(std::remove_if(hand.begin(), hand.end(), isDouble(number)), hand.end());
return *result;
}
return nullptr;
}
bool Player::hasDouble(int number)
{
auto result = std::find_if(hand.begin(), hand.end(), isDouble(number));
return (result != hand.end()) ? true : false;
}
void Player::discardAll()
{
hand.clear();
}
跟踪:
(gdb) backtrace
#0 0x0000000000401a26 in std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release (this=0x622d10) at /usr/include/c++/5/bits/shared_ptr_base.h:150
#1 0x0000000000401505 in std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count (this=0x7fffffffd548, __in_chrg=<optimized out>) at /usr/include/c++/5/bits/shared_ptr_base.h:659
#2 0x0000000000401368 in std::__shared_ptr<Bone, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr (this=0x7fffffffd540, __in_chrg=<optimized out>) at /usr/include/c++/5/bits/shared_ptr_base.h:925
#3 0x0000000000401384 in std::shared_ptr<Bone>::~shared_ptr (this=0x7fffffffd540, __in_chrg=<optimized out>) at /usr/include/c++/5/bits/shared_ptr.h:93
#4 0x0000000000405ad4 in Game::start (this=0x622030, highest_double=6) at Game.cpp:28
#5 0x0000000000405b8b in Game::start (this=0x622030, highest_double=7) at Game.cpp:39
#6 0x0000000000405b8b in Game::start (this=0x622030, highest_double=9) at Game.cpp:39
#7 0x0000000000405b8b in Game::start (this=0x622030, highest_double=8) at Game.cpp:39
#8 0x0000000000405bb7 in Game::start (this=0x622030, highest_double=9) at Game.cpp:41
#9 0x0000000000405b8b in Game::start (this=0x622030, highest_double=4) at Game.cpp:39
#10 0x0000000000405bb7 in Game::start (this=0x622030, highest_double=5) at Game.cpp:41
#11 0x0000000000405bb7 in Game::start (this=0x622030, highest_double=6) at Game.cpp:41
#12 0x0000000000405bb7 in Game::start (this=0x622030, highest_double=7) at Game.cpp:41
#13 0x0000000000405bb7 in Game::start (this=0x622030, highest_double=8) at Game.cpp:41
#14 0x0000000000405bb7 in Game::start (this=0x622030, highest_double=9) at Game.cpp:41
#15 0x0000000000408360 in main (argc=1, argv=0x7fffffffdaf8) at ChickenFoot.cpp:14
问题就在这里...
std::shared_ptr<Bone> Player::getDouble(int number)
{
auto result = std::find_if(hand.begin(), hand.end(), isDouble(number));
if (result != hand.end()) {
hand.erase(std::remove_if(hand.begin(), hand.end(), isDouble(number)), hand.end());
return *result;
}
return nullptr;
}
您在返回之前擦除该值。你不能那样做。一旦你调用 hand.erase()
,result
(它是一个迭代器)就失效了,*result
是垃圾。
总的来说这个函数很混乱,但我认为你正在为这样的东西拍摄...
std::shared_ptr<Bone> Player::getDouble(int number)
{
auto result_iter = std::find_if(hand.begin(), hand.end(), isDouble(number));
if (result_iter != hand.end()) {
// Saving the shared_ptr stops it from being released when we erase the iterator
std::shared_ptr<Bone> result = *result_iter;
// Remove the bone from hand
hand.erase(result_iter);
return result;
}
return nullptr;
}
让我补充一下我是如何找到这个的,因为它归结为阅读堆栈跟踪。
开始的递归调用是可疑的,但无害。这不是堆栈溢出错误,所以你很酷。
前 4 行表明您在 shared_ptr 的析构函数中遇到错误(意味着它的数据不知何故已损坏)并且该行是 Game.cpp:28
紧随其后的行std::shared_ptr<Bone> hd_bone = (*i)->getDouble(highest_double);
.
这或多或少可以保证您的错误在 getDouble
中,这是一个足够小的函数,您只需专注于它即可找到错误。
此处的错误与无关。在那种情况下,shared_ptr 析构函数调用递归发生。这不会发生在这里, shared_ptr 析构函数只发生一次。这很简单,你的 shared_ptr 数据已损坏。
为发布这么多代码提前致歉...
我正在构建一个名为 Chickenfoot 的类似多米诺骨牌游戏的模拟,在该游戏中,玩家从墓地抽 "Bones" 到他们手中,然后在场地上玩多米诺骨牌。
这是我尝试使用智能指针的第一个程序,我 运行 遇到了一个我似乎无法查明原因的问题。偶尔 运行 宁这个程序给我一个分段错误。下面可以看到 gdb 堆栈跟踪。
我在这里做错了什么?此外,如果我滥用了这些 shared_ptr 实例中的任何一个或可以改进实现,任何建议将不胜感激 - 谢谢!
ChickenFoot.cpp
#include <iostream>
#include <cstdlib>
#include <ctime>
#include "Game.h"
const int NUM_OF_PLAYERS = 4;
int main(int argc, char** argv) {
std::srand(std::time(0));
Game* chickenfoot = new Game(NUM_OF_PLAYERS);
chickenfoot->start(DOMINOES_SET_SIZE);
delete chickenfoot;
return 0;
}
Game.h
#include <memory>
#include <vector>
#include "Boneyard.h"
#include "Player.h"
#include "Field.h"
const int INITIAL_HAND_SIZE = 7;
static const int DOMINOES_SET_SIZE = 9;
const bool DEBUG = false;
class Game {
private:
std::vector< std::shared_ptr<Player> > players;
std::shared_ptr<Boneyard> boneyard;
bool played_rounds[DOMINOES_SET_SIZE]; // This will keep track of which double rounds have already been played
int getHighestUnplayedRound(bool* played);
int getNextHighestUnplayedRound(bool* played, int round);
public:
Game(int num_of_players);
void start(int highest_double);
};
Game.cpp
#include "Game.h"
#include <iostream>
Game::Game(int num_of_players)
{
boneyard = std::make_shared<Boneyard>();
for (int i = 0; i < num_of_players; i++) {
players.emplace_back(std::make_shared<Player>(i));
}
for (int i = 0; i <= DOMINOES_SET_SIZE; i++) {
played_rounds[i] = false;
}
}
void Game::start(int highest_double)
{
if (highest_double < 0) {
return;
} else {
boneyard->initialize();
for (int i = 0; i < INITIAL_HAND_SIZE; i++) {
for (std::vector< std::shared_ptr<Player> >::iterator j = players.begin(); j != players.end(); j++) {
(*j)->draw(boneyard);
}
}
for (std::vector< std::shared_ptr<Player> >::iterator i = players.begin(); i != players.end(); i++) {
if ((*i)->hasDouble(highest_double)) {
std::shared_ptr<Bone> hd_bone = (*i)->getDouble(highest_double);
// Do something here to actually play the game...
played_rounds[highest_double] = true;
break;
}
}
}
for (std::vector< std::shared_ptr<Player> >::iterator i = players.begin(); i != players.end(); i++) {
(*i)->discardAll();
}
if (played_rounds[highest_double]) {
start(getHighestUnplayedRound(played_rounds));
} else {
start(getNextHighestUnplayedRound(played_rounds, highest_double));
}
}
Player.h
#include "Bone.h"
#include "Boneyard.h"
#include <vector>
#include <memory>
class Player {
private:
int id;
std::vector< std::shared_ptr<Bone> > hand;
struct isDouble {
int m_value;
isDouble(int value) : m_value(value) {}
bool operator()(const std::shared_ptr<Bone> b) const {
return (b->getLeft() == m_value && b->isDouble());
}
};
public:
Player(int id);
void draw(std::shared_ptr<Boneyard> yard);
std::shared_ptr<Bone> getDouble(int number);
bool hasDouble(int number);
void discardAll();
};
Player.cpp
#include <iostream>
#include <algorithm>
#include "Player.h"
...
std::shared_ptr<Bone> Player::getDouble(int number)
{
auto result = std::find_if(hand.begin(), hand.end(), isDouble(number));
if (result != hand.end()) {
hand.erase(std::remove_if(hand.begin(), hand.end(), isDouble(number)), hand.end());
return *result;
}
return nullptr;
}
bool Player::hasDouble(int number)
{
auto result = std::find_if(hand.begin(), hand.end(), isDouble(number));
return (result != hand.end()) ? true : false;
}
void Player::discardAll()
{
hand.clear();
}
跟踪:
(gdb) backtrace
#0 0x0000000000401a26 in std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release (this=0x622d10) at /usr/include/c++/5/bits/shared_ptr_base.h:150
#1 0x0000000000401505 in std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count (this=0x7fffffffd548, __in_chrg=<optimized out>) at /usr/include/c++/5/bits/shared_ptr_base.h:659
#2 0x0000000000401368 in std::__shared_ptr<Bone, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr (this=0x7fffffffd540, __in_chrg=<optimized out>) at /usr/include/c++/5/bits/shared_ptr_base.h:925
#3 0x0000000000401384 in std::shared_ptr<Bone>::~shared_ptr (this=0x7fffffffd540, __in_chrg=<optimized out>) at /usr/include/c++/5/bits/shared_ptr.h:93
#4 0x0000000000405ad4 in Game::start (this=0x622030, highest_double=6) at Game.cpp:28
#5 0x0000000000405b8b in Game::start (this=0x622030, highest_double=7) at Game.cpp:39
#6 0x0000000000405b8b in Game::start (this=0x622030, highest_double=9) at Game.cpp:39
#7 0x0000000000405b8b in Game::start (this=0x622030, highest_double=8) at Game.cpp:39
#8 0x0000000000405bb7 in Game::start (this=0x622030, highest_double=9) at Game.cpp:41
#9 0x0000000000405b8b in Game::start (this=0x622030, highest_double=4) at Game.cpp:39
#10 0x0000000000405bb7 in Game::start (this=0x622030, highest_double=5) at Game.cpp:41
#11 0x0000000000405bb7 in Game::start (this=0x622030, highest_double=6) at Game.cpp:41
#12 0x0000000000405bb7 in Game::start (this=0x622030, highest_double=7) at Game.cpp:41
#13 0x0000000000405bb7 in Game::start (this=0x622030, highest_double=8) at Game.cpp:41
#14 0x0000000000405bb7 in Game::start (this=0x622030, highest_double=9) at Game.cpp:41
#15 0x0000000000408360 in main (argc=1, argv=0x7fffffffdaf8) at ChickenFoot.cpp:14
问题就在这里...
std::shared_ptr<Bone> Player::getDouble(int number)
{
auto result = std::find_if(hand.begin(), hand.end(), isDouble(number));
if (result != hand.end()) {
hand.erase(std::remove_if(hand.begin(), hand.end(), isDouble(number)), hand.end());
return *result;
}
return nullptr;
}
您在返回之前擦除该值。你不能那样做。一旦你调用 hand.erase()
,result
(它是一个迭代器)就失效了,*result
是垃圾。
总的来说这个函数很混乱,但我认为你正在为这样的东西拍摄...
std::shared_ptr<Bone> Player::getDouble(int number)
{
auto result_iter = std::find_if(hand.begin(), hand.end(), isDouble(number));
if (result_iter != hand.end()) {
// Saving the shared_ptr stops it from being released when we erase the iterator
std::shared_ptr<Bone> result = *result_iter;
// Remove the bone from hand
hand.erase(result_iter);
return result;
}
return nullptr;
}
让我补充一下我是如何找到这个的,因为它归结为阅读堆栈跟踪。
开始的递归调用是可疑的,但无害。这不是堆栈溢出错误,所以你很酷。
前 4 行表明您在 shared_ptr 的析构函数中遇到错误(意味着它的数据不知何故已损坏)并且该行是 Game.cpp:28
紧随其后的行std::shared_ptr<Bone> hd_bone = (*i)->getDouble(highest_double);
.
这或多或少可以保证您的错误在 getDouble
中,这是一个足够小的函数,您只需专注于它即可找到错误。
此处的错误与