• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Zabawa ze wskaźnikami

Object Storage Arubacloud
0 głosów
370 wizyt
pytanie zadane 21 czerwca 2017 w C i C++ przez niezalogowany

Mam kilka pytań odnośnie wskaźników. Najpierw kod:

#include <iostream>
#include <vector>

using namespace std;

class Klasa
{
public:

    int l;

    Klasa()
    {
        l = 3;
    }

    ~Klasa()
    {
        cout << "Destruktor klasy" << endl;
    }
};

int main()
{
    //////////////////////////////////////////////////

    int *wsk1 = new int(1);
    int **wsk2 = &wsk1;
    cout << "*wsk2 = " << *wsk2 << endl << "**wsk2 = " << **wsk2 << endl;

    //////////////////////////////////////////////////

    Klasa ob1;
    vector <Klasa*> wektor_obiektow;
    wektor_obiektow.push_back(&ob1);

    cout << "wektor[0] -> l = " << wektor_obiektow[0] -> l << endl;
    cout << "rowniez ob1.l = " << ob1.l << endl;

    wektor_obiektow[0] -> l = 777;
    cout << "Po zmianie wartosci obiektu w wektorze:" << endl;
    cout << "wektor[0] -> l = " << wektor_obiektow[0] -> l << endl;
    cout << "rowniez ob1.l = " << ob1.l << endl;

    //////////////////////////////////////////////////

    Klasa *ob;
    ob = new Klasa;
    vector <Klasa**> wektor;
    wektor.push_back(&ob);

    cout << "(*wektor[0]) = " << (*wektor[0]) << endl;
    cout << "(**wektor[0]).l = " << (**wektor[0]).l << endl;

    (**wektor[0]).l = 88;
    cout << "Po zmianie wartosci obiektu w wektorze:" << endl;
    cout << "(**wektor[0]).l = " << (**wektor[0]).l << endl;
    cout << "rowniez ob->l = " << ob->l << endl;

    //////////////////////////////////////////////////

    return 0;
}

Podzieliłem kod na 3 bloki. W drugim bloku dodaję do wektora adres obiektu i tylko raz na ekranie wyświetla się "Destruktor klasy", ponieważ nie ma robienia żadnej kopii tylko jest jeden i ten sam obiekt. I tak się zastanawiam czy jest możliwość dodawania do wektora danych, tak że po zniszczeniu wektora te dane są niszczone. Jak widać w bloku 2, można tak zrobić, ale tam tworzę zwykły obiekt, a nie wskaźnik, więc on i tak jest niszczony po wykonaniu programu.

Ja bym chciał mieć możliwość dodawać do wektora wskaźniki i po zniszczeniu wektora te obiekty by były niszczone, gdyż mógłbym wtedy wskaźniki w klasie dodawać do wektora, który po zakończeniu programu jest niszczony i nie byłby wtedy potrzebny destruktor. Próbowałem osiągnąć to w bloku 3, ale tam z kolei niszczony jest tylko wskaźnik, a nie sam obiekt. Pewnie ten dziwny pomysł, na który wpadłem to tylko mrzonki bo gdyby to było możliwe to pewnie dawno by się z tego korzystało, ale tak chciałem spytać.

BTW Czy w Qt jak dodaję obiekt podrzędny do nadrzędnego i nadrzędny jest niszczony to podrzędny automatycznie też?

Np. dodaję do okna głównego dwa widżety, a każdy z nich składa się z kilku przycisków, editów etc.(do których wskaźniki mam w pliku nagłówkowym). Teraz jak okno główne zostanie zniszczone to wszystkie jego widżety-dzieci też? Jest taki mechanizm albo jakiś odśmiecacz czy coś? Czy jednak muszę pamiętać, aby zawsze w destruktorze wszystko deletować? Bo w tutorialach na youtube lub pisanych nie widziałem żeby ktoś się przejmował jakimiś delete mimo, że przecież całe Qt to głównie wskaźniki.

 

3 odpowiedzi

+2 głosów
odpowiedź 21 czerwca 2017 przez unknown Nałogowiec (39,560 p.)
edycja 21 czerwca 2017 przez unknown

Nie wiem czy dobrze zrozumiałem co chcesz osiągnąć, bo trochę chaotycznie to opisałeś.

 tak się zastanawiam czy jest możliwość dodawania do wektora danych, tak że po zniszczeniu wektora te dane są niszczone.

 Ja bym chciał mieć możliwość dodawać do wektora wskaźniki i po zniszczeniu wektora te obiekty by były niszczone

Przechowuj obiekty albo unique_ptr 

0 głosów
odpowiedź 21 czerwca 2017 przez Buby Pasjonat (19,590 p.)

Hej! smiley Oczywiście, że jest taka możliwość, ale wszystko po kolei. Zacznijmy od tego, że w bloku numer 3 doprowadzasz do wycieku pamięci, ponieważ nie używasz operatora delete, dla wcześniej zaalokowanego obiektu operatorem new.

Operatory te powinny być używane jako para - to na barkach programisty leży odpowiedzialność za zwolnienie pamięci - pamiętaj o tym.

Chcesz alokować dynamicznie pamięć, tj. przy pomocy operatora new, a później nie dbać o ich usunięcie - jest to jak najbardziej możliwe - możesz użyć inteligentnych wskaźników. Widzisz, destruktor klasy Vector, wywołuje zapewne instrukcję delete, która ma za zadanie usunąć pamięć, zaalokowaną dla tablicy elementów [vector to kontener korzystający z dynamicznego przydziału pamięci]. Ta z kolei wywołuje destruktory obiektów, które są w nim przechowywane. Niestety destruktor wskaźnika prostego nie używa na nim instrukcji delete, z prostego względu - możesz do niego przypisać jakikolwiek adres - nawet zmiennej, która nie została dynamicznie zaalokowana.

Aby zapewnić, że obiekty posiadają destruktor, który wywułuje instrukcję delete, możemy skorzystać z inteligentnych wskaźników - np. shared_ptr, unique_ptr, bądź weak_ptr. Oczywiście pamiętając, że możemy aplikować do ich konstruktorów tylko i wyłącznie adresy otrzymane przez new. Tutaj artykuł [LINK]

Klasy te, posiadają przeciążone operatory, które pozwalają korzystać z nich niemal jak ze zwykłych wskaźników. Musisz mieć jednak na uwadze, że są to klasy, dlatego są troszeczkę mniej wydajne niż ręcznie stosowane new/delete.

Informacje znajdziesz w artykule, poza tym polecam opis i przykłady z ksiązki C++ Biblioteka Standardowa.

Przykładowy kod może wyglądać tak:

#include <iostream>
#include <vector>
#include <memory> //dla klasy unique_ptr

//Klasa "Krzykacza, ktorej uzyje, zeby pokazac, ze pamiec jest zwalniana
class MyClass {
    int id;
public:
    MyClass(int id_) : id(id_) {
        std::cout << "CONSTRUCTOR: " << id << std::endl;
    }

    ~MyClass() {
        std::cout << "DESTRUCTOR: " << id << std::endl;
    }

    //metoda, zeby pokazac, ze unique_ptr pozwala operowac jak na zwyklym wskazniku za pomoca operatora ->
    void introduce() {
        std::cout << "OBJECT WITH ID: " << id << std::endl;
    }
};

int main() {
    //Tworzenie vectora i umieszczenie w nim 3 obiektow za pomoca dynamicznej alokacji
    //uzywam emplace_back, ktora wywoluje konstruktor unique_ptr z argumentem adresu zwroconego przez new
    //wynika to stad, ze unique_ptr nie posiada operatora przypisania, dla argumentu (const unique_ptr&), z ktorego korzysta vector
    std::vector<std::unique_ptr<MyClass>> myVector;
    myVector.emplace_back(new MyClass(1));
    myVector.emplace_back(new MyClass(2));
    myVector.emplace_back(new MyClass(3));

    //Uzycie metody 2 elementu vectora, za pomoca wskaznika
    std::cout << "\n\nWywoluje metode: " << std::endl;
    myVector[1]->introduce();
    std::cout << "\n\n";
    return 0;
}

W razie pytań pisz - powyższy kod działa w standardzie C++11, bo wraz z nim dostarczono inteligentne wskaźniki o których mówiłem. Pozdrawiam! cheeky

1
komentarz 21 czerwca 2017 przez niezalogowany
Wow, im bardziej poznaję C++, tym bardziej mnie on zadziwia, jest jeszcze lepszy niż myślałem.
1
komentarz 21 czerwca 2017 przez criss Mędrzec (172,590 p.)
I jeszcze lepszy niż myślisz teraz :D
komentarz 21 czerwca 2017 przez Buby Pasjonat (19,590 p.)

Zgadzam się w zupełności yes

0 głosów
odpowiedź 21 czerwca 2017 przez niezalogowany
Mimo, że dowiedziałem się o inteligentnych wskaźnikach i na pewno bardzo mi się przydadzą to jeszcze bym chciał się dowiedzieć o tym Qt. Bo jeśli nie trzeba w nim zwalniać pamięci to nie będę musiał używać tam nawet tych inteligentnych wskaźników (chociaż niewątpliwie przydadzą się przy wielu innych okazjach).
komentarz 21 czerwca 2017 przez Buby Pasjonat (19,590 p.)

Po wygooglowaniu przekazuję dalej informację - tutaj link ze stackoverflow.

Metoda addWidget pamięta o zwolnieniu pamięci, tzn. jeśli usuwany jest "rodzic", to on jest odpowiedzialny za usunięcie swoich dzieci, czyli nie musisz używać inteligentnych wskaźników w przypadku, gdy użyjesz na nich metody addWidget.

Nie traktuj jednak moich słów jako eksperta - nie tworzę w QT, postarałem się znaleźć po prostu tą informację.

 

Podobne pytania

0 głosów
0 odpowiedzi 326 wizyt
pytanie zadane 20 stycznia 2022 w C i C++ przez wyntia Nowicjusz (120 p.)
0 głosów
1 odpowiedź 93 wizyt
pytanie zadane 4 maja 2020 w C i C++ przez Quba Użytkownik (870 p.)

92,575 zapytań

141,424 odpowiedzi

319,649 komentarzy

61,960 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...