Skocz do zawartości

[C++] Usuwanie Obiektów/Klas


Miszcz

Rekomendowane odpowiedzi

Witam.

Napisalem program skladajacy sie z dwóch klas MainWindow i BazaDanych. Pierwsza klasa ma 2 przyciski- jeden po nacisnieciu tworzy ta druga i wypelnia(szczególy w kodzie), natomiast drugi usuwa ja. Z tym, ze no wlasnie nie do konca usuwa, bo pamieci zwalnia sie tyle co nic(w menedzeze pokazuje, ze usuwa niecale 2 mega z 50mb które ta klasa stworzyla) . I tu wlasnie rodzi sie pytanie jak usunac dany obiekt/klase ze wszystkim co ona stworzyla dynamicznie. W moim przypadku sa to obiekty QStandardItems. Wiem, ze QT ma cos takiego, ze jak sie usuwa rodzica to automatycznie usuwa jego "dzieci", ale chyba nie w tym przypadku.

 

 

 

 

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QPushButton>
#include <QHBoxLayout>
#include "bazadanych.h"


namespace Ui {
   class MainWindow;
}

class MainWindow : public QMainWindow
{
   Q_OBJECT

   QHBoxLayout *layout;
   BazaDanych *baza;
   QPushButton *button1,*button2;

private slots:
   void createBase();
   void deleteBase();



public:
   explicit MainWindow(QWidget *parent = 0);
   ~MainWindow();

private:
   Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H

 

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
   QMainWindow(parent),
   ui(new Ui::MainWindow)
{
   ui->setupUi(this);

   baza = 0;

   button1 = new QPushButton("stworz",this);
   button2 = new QPushButton("usun",this);

   layout = new QHBoxLayout;
   layout->addWidget(button1);
   layout->addWidget(button2);

   centralWidget()->setLayout(layout);

   connect(button1,SIGNAL(clicked()),this,SLOT(createBase()));
   connect(button2,SIGNAL(clicked()),this,SLOT(deleteBase()));

}

void MainWindow::createBase(){

   if(!baza)                                        //Stworzenie klasy baza
       baza = new BazaDanych();
           baza->CreateModel();                    //wypelnienie jej


}


void MainWindow::deleteBase(){
     if(baza)
       {
           delete baza;                  //Usuwanie  tej klasy nie usuwa jej wszystkich elementów?
               baza = 0;

       }



MainWindow::~MainWindow()
{
   delete ui;
}

 

 

#ifndef BAZADANYCH_H
#define BAZADANYCH_H

#include <QObject>
#include <QStandardItem>
#include <QStandardItemModel>

class BazaDanych : public QObject
{
   Q_OBJECT


    QStandardItem *itemMark,*itemEngineSize,*itemPower,*itemMileage,*itemColour,*itemDamage,*itemYear;
    QStandardItemModel *model;
   int rowcount;


public:
   explicit BazaDanych(QObject *parent = 0);
   void CreateModel();
   ~BazaDanych();


signals:

public slots:

};

#endif // BAZADANYCH_H

 

 

 

#include "bazadanych.h"

BazaDanych::BazaDanych(QObject *parent) :
   QObject(parent)
{

   model = 0;


}


void BazaDanych::CreateModel(){

   if(!model){
       model = new QStandardItemModel;
           model->insertRow(1);
   }

 for (int i = 0; i < 50000; i++){
   rowcount = model->rowCount();
                                                                                //wypelnianie modelu przez QStandardItem
   model->setItem(rowcount,0,itemMark = new QStandardItem("jakas wartosc"));
   model->setItem(rowcount,1,itemEngineSize = new QStandardItem("jakas wartosc"));
   model->setItem(rowcount,2,itemPower = new QStandardItem("jakas wartosc"));
   model->setItem(rowcount,3,itemYear = new QStandardItem("jakas wartosc"));
   model->setItem(rowcount,4,itemMileage = new QStandardItem("jakas wartosc"));
   model->setItem(rowcount,5,itemColour = new QStandardItem("jakas wartosc"));
   model->setItem(rowcount,6,itemDamage = new QStandardItem("jakas wartosc"));
}

}


BazaDanych::~BazaDanych(){

   model->clear();   //Czysci model, ale nie usuwa QStandardItem?
   delete model;     //Tak samo nie usuwa QStandardItem.

}

 

 

 

Druga sprawa to taka, ze zgodnie z tym co wygooglowalem funkcja(w moim wypadku) model->clear() powinna usuwac równiez QStandardItems. Dlaczego tak sie nie dzieje?

 

 

 

 

EDIT: Znalazlem taka informacje:

 

QStandardItemModel::~QStandardItemModel ()

Destructs the model. The model destroys all its items.

 

 

Jak to uzyc/zaimplementowac?

Odnośnik do komentarza
Udostępnij na innych stronach

Jeśli podany kod reprezentuje to co chciałeś zaprogramować i jedyne wątpliwości budzi zwalnianie pamięci to ja mam tutaj pewną teorię. Najprawdopodobniej delete na wskaźniku do obiektu modelu kasuje itemy, jednak Qt zapewne posiada wewnętrzne metody alokacji danych i nie zwalnia pamięci by później znowu korzystać z puli pamięci bez konieczności ponownej alokacji. Tak przypuszczam. Możesz ewentualnie pobawić się programem valgrind, który identyfikuje wycieki pamięci. Może on tą teorię utwierdzi, lub wskaże ewentualny problem.

Odnośnik do komentarza
Udostępnij na innych stronach

Najprawdopodobniej delete na wskazniku do obiektu modelu kasuje itemy, jednak Qt zapewne posiada wewnetrzne metody alokacji danych i nie zwalnia pamieci by pózniej znowu korzystac z puli pamieci bez koniecznosci ponownej alokacji. Tak przypuszczam.

Moja pierwsza mysl byla podobna, ale tak zachowuje sie garbage collector, a nie jezyk, w którym zarzadzanie pamieci jest manualne. Jednak tu mamy do czynienia z duzym frameworkiem i nigdy nie wiadomo co do konca tam sie dzieje bez czytania dokumentacji ;) Jednak warto to sprawdzic.

Najlatwiej bedzie jak sobie ustawisz dwa klawisze na createBase i deleteBase. Potem uruchom program (ale w zoptymalizowanym trybie, a nie debug!) i twórz baze, a potem ja usuwaj parokrotnie obserwujac zmiany w pamiec po kazdej operacji. Jesli za drugim, trzecim itd. razem zuzycie pamieci bedzie utrzymywalo sie na tym samym poziomie, bez wyraznych wzrostów to jest tak jak napisal Lukasz69.

 

EDIT: wszystko co rozszerza QObject powinno sie zachowywac tak jak napisales, czyli rodzic zajmuje sie wykonczeniem dzieci.

Odnośnik do komentarza
Udostępnij na innych stronach

Chyba jest tak jak mówicie. Sprawdziłem- jak tworzę i usuwam bazę naprzemiennie to pamieć nie wzrasta, ale też nie maleje, utrzymuje się na stałym poziomie. Czyli to całkiem normalne zachowanie?Jest jakiś sposób, żeby jednak tą pamieć zwolnić?

Choć z drugiej strony nie wiem co o tym już myśleć na innym forum napisali mi cos takiego:

 

 

for (int i = 0; i < 50000; i++){

rowcount = model->rowCount();

model->setItem(rowcount,0,itemMark = new QStandardItem("jakas wartosc"));

 

 

Zawsze dodajesz wartosci do tej samej pozycji? 50 tysiecy razy? na pewno nie chciales zamiast rowcount do funkcji setitem przekazac "i" jako pierwszy argument? Nastepuje wyciek pamieci. Przydzielasz pamiec dla 49 999 x 6 niepotrzebnych standard itemow. One wyladuja w pamieci ale tracisz do nich dostep. Dostep masz tylko do jednego

 

Czyli wyciek pamięci?

Odnośnik do komentarza
Udostępnij na innych stronach

Chyba jest tak jak mówicie. Sprawdzilem- jak tworze i usuwam baze naprzemiennie to pamiec nie wzrasta, ale tez nie maleje, utrzymuje sie na stalym poziomie. Czyli to calkiem normalne zachowanie?Jest jakis sposób, zeby jednak ta pamiec zwolnic?

Choc z drugiej strony nie wiem co o tym juz myslec na innym forum napisali mi cos takiego:

 

 

 

Czyli wyciek pamieci?

 

Popatrzylem w dokumentacje i widze co nastepuje:

Sets the item for the given row and column to item. The model takes ownership of the item. If necessary, the row count and column count are increased to fit the item. The previous item at the given location (if there was one) is deleted.

 

This function was introduced in Qt 4.2.

Z tego wynika wedlug mnie ze tak (_na prawde_ → naprawde) ORT wstawiasz jeden item(bo rowcount masz w setItem() - gdybys dal i zamiast rowcount to bys wstawil 50000 itemów), item nadpisywany jest wczesniej traktowany poprzez delete.

 

Wstawianie nowych itemów robilbym raczej poprzez void QStandardItemModel::insertRow ( int row, QStandardItem * item ) - jesli dasz pod zmienna row 0 to powinno wtedy dodac Ci nowy element na poczatek modelu.

 

Co do programowania to polecam zaznajomienie sie z Qt po solidnym kursie C++ - z wlasnego doswiadczenia wiem, ze nawet osoby w miare przyzwoicie obeznane z C++ moga miec problemy z Qt - bez dobrej znajomosci C++ nauka Qt bedzie dosc uciazliwa. Na pewno nie chce Cie zniechecac - fajnie ze robisz w Qt ale nie zapomnij o szlifowaniu C++ bo to pomoze.

Odnośnik do komentarza
Udostępnij na innych stronach

Moja pierwsza myśl była podobna, ale tak zachowuje się garbage collector, a nie język, w którym zarządzanie pamięci jest manualne. Jednak tu mamy do czynienia z dużym frameworkiem i nigdy nie wiadomo co do końca tam się dzieje bez czytania dokumentacji ;) Jednak warto to sprawdzić.

Najłatwiej będzie jak sobie ustawisz dwa klawisze na createBase i deleteBase. Potem uruchom program (ale w zoptymalizowanym trybie, a nie debug!) i twórz bazę, a potem ją usuwaj parokrotnie obserwując zmiany w pamięć po każdej operacji. Jeśli za drugim, trzecim itd. razem zużycie pamięci będzie utrzymywało się na tym samym poziomie, bez wyraźnych wzrostów to jest tak jak napisał Lukasz69.

 

EDIT: wszystko co rozszerza QObject powinno się zachowywać tak jak napisałeś, czyli rodzić zajmuje się wykończeniem dzieci.

Visual Studio czy Borland mają swoje biblioteki i one też korzystają z tzw. Parenta lub Ownera(w zależności od biblioteki mogą mieć różne znaczenia), nie nazywałbym tego garbage collectorem a raczej zależnościowym zwalnianiem pamięci(garbage collector oddzyska zgubiony wskaźnik - mechanizmy pod nazwą Parent/Owner nie zadziałają gdy jawnie, gdzieś nie zainicjujemy zwolnienia obiektu, który ma rolę roota zależności). Co do frameworków to Qt zdaje się korzysta z puli pamięci - pozwala to przyśpieszyć działanie operacji new/delete oraz powoduje lepszą segmentację danych - gdyby popatrzeć bliżej to zapewne sporo bibliotek korzysta z tej techniki.

Odnośnik do komentarza
Udostępnij na innych stronach

W ogóle to tu wyciek jest bardzo możliwy, bo dodajesz jeden wiersz na pozycji 1 (model->insertRow(1)), a potem używasz setItem() na indeksie 50000, który ustawia wartość tylko na 50000 wierszu, a nie wstawia go na tę pozycję. Tak jak napisał Lukasz69 powinieneś używać wyłącznie insertRow, a setItem jak modyfikujesz wartość istniejącego wiersza. Jeśli nawet nie ma wycieku to wszystkie elementy są wstawiane na ostatnią pozycję (50000), więc tak naprawdę te 2MB zwolnione to mogą być właśnie te jedyne elementy w tej liście znajdujące się na końcu.

 

Jest jakiś sposób, żeby jednak tą pamieć zwolnić?

Taki rzeczami zajmuje się kernel, kiedy pewne mechanizmy zauważą, że pamięć się kończy lub jest niepotrzebnie zajmowana. Nie musisz tym się martwić. W ogóle za dziwną rzecz się złapałeś. Jak będziesz coś sobie pisał to rzeczy typu zarządzanie pamięcią same przyjdą z czasem. Najczęściej program pod kątem wycieków pamięci sprawdza się już po napisaniu czegoś. Trudno jest to stale kontrolować.

 

Co do frameworków to Qt zdaje się korzysta z puli pamięci - pozwala to przyśpieszyć działanie operacji new/delete oraz powoduje lepszą segmentację danych - gdyby popatrzeć bliżej to zapewne sporo bibliotek korzysta z tej techniki.

W ogóle to teraz dopiero zauważyłem, że tu są głównie wykonywane operacje na jakiejś kolekcji, np. lista, drzewo. Kolekcje mają to do siebie, że są zarządzane dynamicznie i nie mają stałego rozmiaru. A pula to może być po prostu pamięć wirtualna, która jest przydzielana każdemu procesowi.
Odnośnik do komentarza
Udostępnij na innych stronach

Jeśli chcesz dodać odpowiedź, zaloguj się lub zarejestruj nowe konto

Jedynie zarejestrowani użytkownicy mogą komentować zawartość tej strony.

Zarejestruj nowe konto

Załóż nowe konto. To bardzo proste!

Zarejestruj się

Zaloguj się

Posiadasz już konto? Zaloguj się poniżej.

Zaloguj się
×
×
  • Dodaj nową pozycję...