決定木を表すモジュールがあります。Choice (外部クラス Event から継承) と Option の 2 つのクラスがあります。Choice は決定木のノードを表し、Option は分岐を表します。Choice には少なくとも 1 つの Option が必要です。Option は Choice を持つことも、持たないこともできます。Option に Choice がない場合、それは最終的な Option です。
たとえば、デシジョン ツリーが次のようになっているとします。
A----B
|
----C----D
|
-----E
A は、2つ
のオプション (B と C) を持つ選択肢になります。
B は、選択肢のないオプション (つまり、ターミナル オプション) になります。
C は、選択肢を持つオプションになります。C's Choice には、オプション D と E が含まれます。
意思決定ツリーを必要なだけ深くできるようにコードを作成しました。これが、オプションに選択肢があり、選択肢にオプションがある理由です。
関数 function find_terminal_options_in(EventPtr ch) があり、すべての端末オプションを見つけてその名前を取得する再帰呼び出しがあります。この例では、find_terminal_options_in(ptr_to_A) は {"B","D","E"} を返す必要があります。代わりに、オプション C の選択を処理している 2 番目の呼び出しの最後に失敗します。次のエラーが発生して失敗します。
デバッグ アサーションに失敗しました!
式: _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;
}