Operator= i kopiowanie wskaźników

0

Witajcie. Wczoraj natrafiłem na pewien problem dotyczący kopiowania wskaźników. Najlepiej problem przedstawi poniższy kod

#include <iostream>

using namespace std;
class Foo
{
public:
    int a, b, c, d;
    Foo() {a = 0, b = 2, c = 5, d = 4;}
};
class Foo2
{
public:
    Foo* f;
    Foo2() {f = NULL;}
    Foo2(Foo* _f)
    {
        f = _f;
    }
    Foo2& operator=(const Foo2& f2)
    {
        this->f = f2.f;
    }
    void doSthWithF()
    {
        cout << f->b; //proba odczytania z juz nie istniejacego f
    }
    ~Foo2()
    {
        delete f;
    }
};
int main()
{
    Foo* f = new Foo();
    Foo2 f2; //tworzymy obiekt klasy Foo2 wywołując domyślny konstruktor
    f2 = Foo2(f); //przypisujemy do f2 obiekt klasy Foo2 wywołany z konstruktorem przyjmującym Foo*
    //spoko. Tylko co sie stanie jesli przypiszemy ten wskaznik do f2.f, a potem wykonamy dekstuktor na obiekcie tymczasoym?
    f2.doSthWithF();      //to wypisze smieci lub będzie naruszenie ochrony pamięci
    return 0;
}

Pytanie brzmi. Jak uniknąć takich skutków? Czy może tak się po prostu nie robi i są inne, bardziej poprawne sposoby na to co chciałem zrobić w powyższym kodzie?

Byłbym wdzięczny za wszystkie odpowiedzi, bo mi nic do głowy nie przychodzi.

PS. W google nie szukałem, bo kompletnie nie wiedziałem jakiej frazy użyć

1

Sprawa jest prosta, obejrzyj jak zrobiona klasa string w STL. Możesz też użyć inteligentnego wskaźnika, lub sam go zaimplementować.

1

Może wyjaśnij co chcesz osiągnąć dokładnie? No chyba, że to takie teoretyczne rozważania?

Większości dziwnych problemów ze wskaźnikami możesz uniknąć stosując smart pointery, w bibliotece standardowej jest ich kilka. Możesz też samemu napisać liczenie referencji i np. powiązane z tym copy-on-write (to jest to coś, czego używa std::string w implementacji libstdc++, nie wiem jak w innych), ale równie dobrze może to załatwić pewniestd::shared_ptr.

0

Widzę, że odpowiedź już uzyskałem, za co dziękuję, jednak żeby nie było wątpliwości wrzucę jeszcze lekko poprawiony przykład. Tamten był nieco niejasny i spodziewałem się odpowidzi "delete powienien być w main".
już się poprawiam

#include <iostream>

using namespace std;
class Foo
{
public:
    int a, b, c, d;
    Foo() {a = 0, b = 2, c = 5, d = 4;}
};
class Foo2
{
public:
    Foo* f;
    Foo2() {f = NULL;}
    Foo2(int i)
    {
        f = new Foo[i]; //alokacja obiektow klasy Foo
    }
    Foo2& operator=(const Foo2& f2)
    {
        this->f = f2.f;
    }
    void doSthWithF()
    {
        cout << f[0].b; //proba odczytania z juz nie istniejacego f[0]
    }
    ~Foo2()
    {
        delete[] f;
    }
};
int main()
{
    Foo2 f2; //tworzymy obiekt klasy Foo2 wywołując domyślny konstruktor
    f2 = Foo2(16); //przypisujemy do f2 obiekt klasy Foo2 podając ilość obiektów Foo jakie ma zawierać
    //spoko. Tylko co sie stanie jesli przypiszemy ten wskaznik do f2.f, a potem wykonamy dekstuktor na obiekcie tymczasoym?
    f2.doSthWithF();      //to wypisze smieci lub będzie naruszenie ochrony pamięci
    return 0;
}
 

Wcześniej można było (jak napisałem wcześniej) deletować obiekt f w main. Teraz wszystko jest tworzone w Foo2 więc takiej możliwości nie ma.\

Dziękuję jeszcze raz za odpowiedź, poczytam o tym.

PS.

No chyba, że to takie teoretyczne rozważania?

Nie. Problem jest jak najbardziej prawdziwy. Wczoraj podczas pisania z nudów prostej konsolówki straciłem na nim pół godziny życia i rozwiązałem go brzydkim usunięciem destruktorów...

0

Ten kod dalej nie jest dobry. W operatorze przypisania kopiujesz tylko wskaźnik, kiedy jeden z tych obiektów zniknie wtedy wykona się delete. Do takich rzeczy właśnie jest std::shared_ptr.

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