我有一个代表决策树的模块。我有两个类:Choice(从外部类 Event 继承)和 Option。Choice 代表决策树的一个节点,Option 代表一个分支。一个选项必须至少有一个选项。一个选项可以有一个选择或没有。如果选项没有选项,则它是终端选项。
例如,如果决策树看起来像这样:
A----B
|
----C----D
|
-----E
那么:
A 将是一个具有两个选项(B 和 C)的选项。
B 将是没有选择的选项(即终端选项)。
C 将是一个有选择的选项。C 的选择将包含选项 D 和 E。
我编写了我的代码以允许决策树尽可能深,这就是为什么选项有选择和选择有选项的原因。
我有一个函数函数 find_terminal_options_in(EventPtr ch) ,它带有一个递归调用,可以找到所有终端选项并获取它们的名称。在此示例中,find_terminal_options_in(ptr_to_A) 应返回 {"B","D","E"}。相反,它在第二次调用结束时失败,此时它正在处理选项 C 的选择。它通过给出以下错误而失败:
调试断言失败!
表达式:_BLOCK_TYPE_IS_VALID(pHead->nBlockUse)
在 shared_ptr 析构函数中调用。
发生此错误是由于我的设计缺陷还是我使用 shared_ptr 的方式存在缺陷?有关如何摆脱此运行时错误的任何建议?
请参阅我的(简化的)代码,它重现了该问题:
class Event {
public:
Event(std::string name):name_(name) {};
std::string name() {return name_;};
virtual bool is_terminal() = 0;
protected:
std::string name_;
};
class Option;
class Choice: public Event {
public:
Choice(): Event("") {};
Choice(std::string name, std::list<Option> options): Event(name) {options_ = options;};
std::list<Option> options() {return options_;};
std::string name() {return name_;};
bool is_terminal() {return false;};
private:
std::list<Option> options_;
};
class Option
{
public:
Option(std::string name, Choice choice):name_(name),choice_(choice) {};
Option(std::string name):name_(name) {};
Choice choice() {return choice_;};
std::string choice_name() {return choice_.name();};
std::string option_name() {return name_;};
private:
Choice choice_;
std::string name_;
};
typedef std::shared_ptr<Event> EventPtr;
typedef std::shared_ptr<Event> ChoicePtr;
std::list<std::string> find_terminal_options_in(EventPtr ch);
int main() {
std::list<Option> temp_opts1;
temp_opts1.push_back(Option("D"));
temp_opts1.push_back(Option("E"));
Choice option_Cs_choice("option_Cs_choice",temp_opts1);
std::list<Option> temp_opts2;
temp_opts2.push_back(Option("C",option_Cs_choice));
temp_opts2.push_back(Option("B"));
EventPtr ptr_to_A = EventPtr(new Choice("A",temp_opts2));
std::list<std::string> terminal_options = find_terminal_options_in(ptr_to_A);
}
std::list<std::string> find_terminal_options_in(EventPtr ch)
{
std::list<std::string> returned_list;
std::shared_ptr<Choice> choice = std::dynamic_pointer_cast<Choice>(ch);
std::list<Option> choice_options = choice->options();
for(std::list<Option>::iterator options_it = choice_options.begin();options_it != choice_options.end(); options_it++)
{
if(options_it->choice_name() != "") //it has a choice
{
Choice option_choice = options_it->choice();
find_terminal_options_in(EventPtr(&option_choice));
}
else //it doesn't have a choice, and is therefore a terminal option
returned_list.push_back(options_it->option_name());
}
return returned_list;
}