1. ホーム
  2. c++

[解決済み】デバッグアサーションに失敗しました

2022-01-28 16:49:53

質問

プログラム終了時に "Debug assertion failed" というエラーが発生します。ずっと直そうとしているのですが、どうしても原因がわかりません。大学の教授でさえ、何も問題ないと言っています。だから、あなたは私の最後の希望、stackoverllowです。どうか助けてください。

このプログラムは、2つのリストの交点を求め、3番目のリストが交点の部分集合であるかどうかをチェックするものです。

エラーの発生した画面です。

コードです。

list.h:

#ifndef __LIST_H_INCLUDED__
#define __LIST_H_INCLUDED__
#include <string>
#include <iostream>
#include <fstream>

struct node
{
    int value;
    node *next;
};

class list
{
    node* head;
public:
    list();
    ~list();
    void AddNodes(std::istream &input);
    void PrintList(std::ostream &output = std::cout);
    void AddOneNode(int AddVal);
    node* RetHead();
    list* Intersection(list* list2);
    bool IsPresent(int val);
    bool Subset(list subset);
 };

 #endif

list.cpp:

#include "stdafx.h"
#include "list.h"
#include <iostream>
#include <fstream>


list::list()
{
    head=NULL;
}

list::~list()
{

    node* current = head;
    while( current != 0 ) 
    {
        node* next = current->next;
        delete current;
        current = next;
    }
    head = 0;

}

void list::AddNodes(std::istream &input)
{
    int InVal;
    while(input>>InVal)
        AddOneNode(InVal);
}

void list::AddOneNode(int AddVal)
{
    node *NewNode= new node;
    NewNode->value=AddVal;
    NewNode->next=NULL;
    if(!head)
        head=NewNode;
    else
        {
            node *temp=head;
            while(temp->next)
                temp=temp->next;
            temp->next=NewNode;
        }
}

void list::PrintList(std::ostream &output)
{
    node *temp=head;
    while(temp)
    {
        output<<temp->value<<std::endl;
        temp=temp->next;

    }
}

list* list::Intersection(list *list2)
{
    list* result=new list;
    node* temp1=head;
    while(temp1)
    {
        if(list2->IsPresent(temp1->value))
            result->AddOneNode(temp1->value);
        temp1=temp1->next;

    }
    return result;
}

bool list::IsPresent(int val)
{
    node *temp=head;
    while(temp)
    {
        if(temp->value==val)
            return true;
        temp=temp->next;
    }
    return false;
}


bool list::Subset(list subset) // head=set
{
    bool flag;
    node* tempset=head;
    node* tempsub=subset.RetHead();
    while(tempset)
    {
        if (tempsub->value==tempset->value)
        {
            flag=true;
            break;
        }
        tempset=tempset->next;
    }
    if (!tempset)
        return false;
    while(tempsub)
    {
        tempsub=tempsub->next;
        if(!tempsub)
            return true;
        while(tempsub->value!=tempset->value&&tempset)
            tempset=tempset->next;
        if(!tempset)
            return false;
    }
    return flag;
}

node* list::RetHead()
{
    return head;
}

main.cpp:

#include "stdafx.h"
#include "list.h"
#include <Windows.h>
#include <fstream>

list Cross (list list1, list list2);
bool Subset (list set, list subset);

int main()
{
    setlocale (LC_ALL, "Russian");
    list l1,l2,l3;
    std::ifstream fl1 ("l1.txt");
    std::ifstream fl2 ("l2.txt");
    std::ifstream fl3 ("l3.txt");
    l1.AddNodes(fl1);
    std::cout<<"List 1:"<<std::endl;
    l1.PrintList();
    std::cout<<std::endl;
    l2.AddNodes(fl2);
    std::cout<<"List 2:"<<std::endl;
    l2.PrintList();
    std::cout<<std::endl;
    l3.AddNodes(fl3);
    std::cout<<"List 3:"<<std::endl;
    l3.PrintList();
    std::cout<<"Intersection of list 1 and list 2"<<std::endl;
    list *intersec=l1.Intersection(&l2);
    intersec->PrintList();
    std::cout<<std::endl;
    if(intersec->Subset(l3))
        std::cout<<"Third set is a subset of the intersection"<<std::endl;
    else
        std::cout<<"Third set is not a subset of the intersection"<<std::endl;
    system("pause");
    return 0;
}

解決方法は?

問題は、関数 list::Subset(list subset) のコピーを引き起こす値によってその引数を取ります。 list を作成します。Chrisのコメントにあるように)「3つの法則」に従っていないので シャローコピー が作られます。これは、2つのインスタンスの list ポインターを所有します。このとき Subset 関数が返ると、コピーがスコープ外になり、ノードが削除されます。プログラムが終了すると、元のコピーである list がスコープ外に出てしまい、同じノード 再び が発生し、アサーションが発生します。

引数を値で取るのではなく、参照で取ることでこれを回避することができます。変更

class list
{
    // ... snip ...
    bool Subset(list subset);
    // ... snip ...
};

になります。

class list
{
    // ... snip ...
    bool Subset(list& subset);
    // ... snip ...
};

そして

bool list::Subset(list subset)
{
    // ... snip ...
}

になります。

bool list::Subset(list& subset)
{
    // ... snip ...
}

その他の提案もあります。

  1. 適切なコピーコンストラクタを実装するか、または宣言してプライベートにし、コピーが作られないようにする。
  2. 学ぶ const の正しさです。から Subset は渡されたリストの内容を変更しないので、それを宣言することができます。 bool list::Subset(const list&) const の代わりに これには list::RetHead() を宣言します。 const にも対応しています。
  3. bool flaglist::Subset は初期化されないので、ロジックが正しくない場合は任意の値が返される可能性があります。