Przeciążanie operatorów i crash

0

Witam wszystkich serdecznie, jako że jest to mój pierwszy post.
Mam problem z następującym zadaniem:

Napisz klasę Set opisującą podzbiór liczb całkowitych. Definicja klasy powinna zawierać implementację następujących funkcji:
konstruktor, przyjmujący dwa parametry (ilość elementów zbioru i tablicę zawierającą te elementy)
konstruktor kopiujący
destruktor
operator +() - suma zbiorów (elementy zawarte w przynajmniej jednym zbiorze)
operator -() - różnica zbiorów (elementy zawarte w pierwszym ale nie w drugim zbiorze)
operator *() - iloczyn zbiorów (elementy zawarte w obu zbiorach)
operator =() - operator przypisania zbiorów
Pamiętaj o usunięciu powtarzających się elementów ze zbiorów wynikowych. Zaimplementuj operator << wypisujący elementy zbioru oraz funkcję main() pokazującą działanie klasy.

Niestety po napisaniu dostaje crash już w momencie sumowania zbiorów, a nie mogę znaleźć w czym jest problem. Prosiłbym o rzucenie okiem na kod:

#include<iostream>
#include<cstdlib>
#include<vector>
#include<ctime>
#include<iomanip>

using namespace std;

class Set {
    private:
        int amnt;
        vector<int> vals;
    public:
        Set(int a): amnt(a) {};
        Set(int a, vector<int> v): amnt(a),vals(v) {};
        Set(const Set& s); 
        ~Set() {   
            vals.clear();
            amnt=0;
        }

        Set operator +(const Set& s) const;
        Set operator -(const Set& s) const;
        Set operator *(const Set& s) const;
        Set& operator =(const Set& s);

        friend bool not_inside(int a, const Set& b);
                friend ostream& operator <<(ostream& os, const Set& s);
                friend void insertionSort(Set& s);
};

bool not_inside(int a, const Set& b) {
    int n=b.amnt;
    int l=0;
    int p=n-1;
    while(l<=p) {
        int s=(l+p)/2;
        if(b.vals[s] == a)
            return false;
        else if (b.vals[s] < a)
            l=s+1;
        else
            p=s-1;
    }
    return true;
}

void insertionSort(Set& s) {

    for(int j=s.amnt-2;j>=0;j--) {
        int x=s.vals[j];
        int i=j+1;
        while((i<s.amnt) && (x>s.vals[i])) {
            s.vals[i-1]=s.vals[i];
            i++;
        }
        s.vals[i-1]=x;
   }
}

Set::Set(const Set& s) {
        amnt = s.amnt;
        for (int i=0;i<amnt;i++)
            vals[i] = s.vals[i];
        }

Set Set::operator +(const Set& s) const{
        int size_min=min(this->amnt,s.amnt);
        int size_max=max(this->amnt,s.amnt);
        Set ret(0);

        for(int i=0;i<min(this->amnt,s.amnt);i++) {
            if(this->vals[i]==s.vals[i]) {
                ret.vals.push_back(s.vals[i]);
                                ret.amnt++;
                        }
            else {
                ret.vals.push_back(this->vals[i]);
                ret.amnt++;
                ret.vals.push_back(s.vals[i]);
                ret.amnt++;
            }
        }

        if(this->amnt != s.amnt)
            for(int j=min(this->amnt,s.amnt);j<size_max;j++) {
                ret.vals.push_back(this->amnt>s.amnt?this->vals[j]:s.vals[j]);
                ret.amnt++;
            }

        insertionSort(ret);
        int i=0;
        while(i<ret.amnt-1) {
            if(ret.vals[i]==ret.vals[i+1]) {
                ret.vals.erase(ret.vals.begin()+i+1);
                ret.amnt--;
            }
            else i++;
        }
        return ret;
}

Set Set::operator -(const Set& s) const{
        Set ret(-1);
        for(int j=0;j<this->amnt;j++)
            if(not_inside(this->vals[j],s)) {
                ret.vals.push_back(this->vals[j]);
                ret.amnt++;
            }
        return ret;
}

Set Set::operator *(const Set& s) const{
    Set ret(0);
    for(int j=0;j<max(this->amnt,s.amnt);j++)
        if(!not_inside((this->amnt>s.amnt?this->vals[j]:s.vals[j]),(this->amnt<s.amnt?*this:s))) {
            ret.vals.push_back(this->amnt>s.amnt?this->vals[j]:s.vals[j]);
            ret.amnt++;
        }
        return ret;
}

Set& Set::operator =(const Set& s) {
    this->vals.clear();
    this->amnt=s.amnt;
    this->vals.resize(sizeof(int)*s.amnt);
    for(int i=0;i<s.amnt;i++)
        this->vals[i]=s.vals[i];
    return *this;
}

ostream& operator <<(ostream& out, const Set& s) {
        out << setprecision(4) << "{";
        for (int i=0;i<s.amnt-1;i++)
            out << setw(10) << s.vals[i] << " ";
        out << setw(10) << s.vals[s.amnt-1] << "}" << endl;
        return out;
}

int main() {
    srand(time(NULL));
    int n=rand()%10;
    vector<int> v1(n), v2(n);
    for(int i=0;i<n;i++) {
        v1[i]=rand()%10;
        v2[i]=rand()%10;
    }
    Set s1(n,v1), s2(n,v2);
    cout.setf(ios::fixed, ios::floatfield);
    cout << endl;
    cout << s1;
    cout << s2;
    Set s3(n);
    s3=s1+s2;
    cout << s3 << endl;
    s3=s3-s1;
    cout << s3 << endl;
    s3=s1*s2;
    cout << s3 << endl;
    return 0;
}

Dzięki wielkie za wszelkie rady.

1
Set::Set(const Set& s) {
                amnt = s.amnt;
                for (int i=0;i<amnt;i++)
                        vals[i] = s.vals[i];
                }

W konstruktorze nie alokujesz miejsca w wektorze, więc nie możesz w ten sposób wprowadzać elementów. Jeśli już to korzystaj z funkcji push_back()

0

czemu nie zrobić po ludzku:

class Set:public vector<int>
  {
   public:
  };

Czyli całość dotyczącą wypełnienia już masz.

0

Dzięki, poszło.

0
_13th_Dragon napisał(a):

czemu nie zrobić po ludzku:

class Set:public vector<int>
{
public:
};

Czyli całość dotyczącą wypełnienia już masz.

Bo dziedziczenia należy unikać i preferować składanie?

0

Eh, jak by ci sie chciało chociaż kilka artykułów przeczytać w tej tematyce. Ale prościej spamować.
Składanie nie jest lepsze, jest preferowane przez OOP, dlaczego? ponieważ zapewnia lżejsze powiązanie między klasami i wymusza inne relacje pomiędzy klasami, mniej spójne przez co łatwiej wymieniać poszczególne bloczki. Dziedziczenie można stosować, kiedy chcemy mieć powiązania na etapie kompilacji i obiekty mają ze sobą silne powiązanie.

0

Chyba że za jakiś czas zmieni implementacje set z vector na chociażby map. Wystawiając własne funkcje wystarczy że zmieni bebechy klasy, w przypadku dziedziczenia już będzie większy kłopot, ktoś moze sie pokusic o używanie bezpośrednio metod z klasy vector.
Odniosłem sie tylko do informacji "dlaczego nie dziedziczenie", bo nie wiemy co autor chciał uzyskać.

0

dla mnie brak wirtualnego destruktora w implementacji std::vector jest wystarczającym dowodem na to iż stl nie został opracowany "w duchu obiektowości".

0

@_13th_Dragon naprawdę nie rozumiem czemu zawsze cie to tak boli jak ktoś ci pokaże inne rozwiązanie? Pytałeś czemu nie użył dziedziczenia, dostałeś odpowiedź że może dlatego że preferuje składanie w myśl OOP?
Powiedziałeś że to bzdury, które ktoś mi nawkładał do głowy(znaczy co nie mogę tak uważać? bo ty masz na ten temat inne zdanie?) ... wysłałem ci linki plus zapytanie do google, którego ci sie chyba nie chciało wpisać, i które omawia to zagadnienie, jak widać nie jest to tylko mój wymysł.
Ty natomiast dalej ciągniesz durne dyskusje.

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0