Varför kan jag inte radera objekt som inte är nullptr i C++?

Permalänk

Varför kan jag inte radera objekt som inte är nullptr i C++?

Jag har ett fält i min klass som heter

QLineSeries* pwm_series[8];

När jag skapar ett objekt från min klass så initialiserar jag fältet med nullptr.

for(uint8_t i = 0; i < 8; i++) pwm_series[i] = nullptr;

När jag ska initialisera fältet flera gånger, så måste jag radera nuvarande objekt.

for(uint8_t i = 0; i < 8; i++){ if(pwm_series[i] != nullptr) delete pwm_series[i]; pwm_series[i] = new QLineSeries(); }

Men det styper här när jag ska sätta in objektet. Det krashar här för att pwm_series[i] är null.

chart->addSeries(pwm_series[0]);

Men om jag inte raderar pwm_series[i]. Då fungerar det riktigt bra. Alltså betyder det att pwm_series[i] är fortfarande null.

for(uint8_t i = 0; i < 8; i++){ if(pwm_series[i] != nullptr) ; //delete pwm_series[i]; pwm_series[i] = new QLineSeries(); }

Notera att jag FÖRST anropar att jag skapar objekten, sedan så implementerar jag dom.

void Logging_tab::add_series_to_chart(){ chart->removeAllSeries(); create_series(); /* PWM */ if(ui->enable_pwm0_checkBox->isChecked()) chart->addSeries(pwm_series[0]); if(ui->enable_pwm1_checkBox->isChecked()) chart->addSeries(pwm_series[1]); if(ui->enable_pwm2_checkBox->isChecked()) chart->addSeries(pwm_series[2]);

Där create_series(); ser ut så här

void Logging_tab::create_series(){ for(uint8_t i = 0; i < 8; i++){ if(pwm_series[i] != nullptr) delete pwm_series[i]; pwm_series[i] = new QLineSeries(); }

Så vad gör jag för fel här?
Varför blir den nullptr trots att jag har satt "new" till objektet?
Notera att det är QT ramverket jag håller på med.

Permalänk
Medlem

Från dokumentationen för QChart::addSeries:

Citat:

Adds the series series to the chart and takes ownership of it.

Du får alltså inte anropa delete på en pekare du anropat addSeries på, den ägs då av QChart:en och den ansvarar själv för att ta bort den när den inte längre behövs.

delete på en nullptr är för övrigt tillåtet och gör ingenting, du behöver inte hålla på och kolla det själv.

Permalänk
Skrivet av perost:

Från dokumentationen för QChart::addSeries:
Du får alltså inte anropa delete på en pekare du anropat addSeries på, den ägs då av QChart:en och den ansvarar själv för att ta bort den när den inte längre behövs.

delete på en nullptr är för övrigt tillåtet och gör ingenting, du behöver inte hålla på och kolla det själv.

Så i detta fall så behöver jag inte bry mig om något? Det är QT som sköter skräpsamlaren (GC) av sig själv?

Permalänk
Medlem
Skrivet av heretic16:

Så i detta fall så behöver jag inte bry mig om något? Det är QT som sköter skräpsamlaren (GC) av sig själv?

Ja, det är ingen riktig GC förstås, men QT har som genomgående tema att man ofta allokerar med new och sen ger över ägarskapet till QT-klassen man använder.

Permalänk
Skrivet av perost:

Ja, det är ingen riktig GC förstås, men QT har som genomgående tema att man ofta allokerar med new och sen ger över ägarskapet till QT-klassen man använder.

Alltså är denna kod helt värdelös? För jag har ett behov att kolla om ett fält har fått ett objekt i sig. Jag brukar oftast sätta fältet till nullptr och om fältet är nullptr, så ska jag inte använda objektet.

private: QSqlTableModel* calibration_model = nullptr; Funktionen: if(calibration_model != nullptr) delete calibration_model; calibration_model = new QSqlTableModel;

Hur menar du med "QT-kalssen"

Menar du att man måste skriva makrot Q_OBJECT i varje klass?

class Database_handling : public QObject { Q_OBJECT public: Database_handling(); private:

Permalänk
Medlem
Skrivet av heretic16:

Alltså är denna kod helt värdelös? För jag har ett behov att kolla om ett fält har fått ett objekt i sig. Jag brukar oftast sätta fältet till nullptr och om fältet är nullptr, så ska jag inte använda objektet.

private: QSqlTableModel* calibration_model = nullptr; Funktionen: if(calibration_model != nullptr) delete calibration_model; calibration_model = new QSqlTableModel;

Ja, om du skickar din calibration_model till t.ex. en QTableView via addModel så är den koden felaktig, eftersom det inte längre är din pekare att delete:a.

I modern C++ brukar man rekommendera att endast använda "nakna" pekare som observerande pekare, d.v.s. pekare till data man vill komma åt men som man inte äger. För ägande pekare använder man std::unique_ptr eller ibland std::shared_ptr om man har flera ägare. Man behöver alltså i stort sett aldrig anropa delete själv i modern C++.

Skrivet av heretic16:

Hur menar du med "QT-kalssen"

Menar du att man måste skriva makrot Q_OBJECT i varje klass?

class Database_handling : public QObject { Q_OBJECT public: Database_handling(); private:

Nej, jag menar bara när du använder någon klass från QT, t.ex. QChart, som tar över ägandeskapet av pekare du skickar in via dess metoder.

Permalänk
Hedersmedlem

Ofta kan man ju också skicka med ett förälder-objekt direkt i konstruktorn (och sedan förlita sig på att detta tar hand om objektet). Det går nog att radera manuellt också, men då bör du plocka ut serien från grafen först.

Permalänk
Skrivet av perost:

Ja, om du skickar din calibration_model till t.ex. en QTableView via addModel så är den koden felaktig, eftersom det inte längre är din pekare att delete:a.

I detta fall är det inget argument, utan funktionen ser ut så här. Då blir det rätt.

void Database_handling::create_new_calibration_model_table_for_tableView(){ if(calibration_model != nullptr) delete calibration_model; calibration_model = new QSqlTableModel; calibration_model->setTable(CALIBRATION_TABLE_NAME); calibration_model->setEditStrategy(QSqlTableModel::OnManualSubmit); calibration_model->sort(0, Qt::SortOrder::DescendingOrder); calibration_model->sort(1, Qt::SortOrder::DescendingOrder); calibration_model->sort(2, Qt::SortOrder::DescendingOrder); calibration_model->sort(3, Qt::SortOrder::DescendingOrder); calibration_model->setHeaderData(0, Qt::Horizontal, tr("ID")); calibration_model->setHeaderData(1, Qt::Horizontal, tr("Calibration ID")); calibration_model->setHeaderData(2, Qt::Horizontal, tr("Calibration comment")); calibration_model->setHeaderData(3, Qt::Horizontal, tr("Calibration date time")); }

Citat:

I modern C++ brukar man rekommendera att endast använda "nakna" pekare som observerande pekare, d.v.s. pekare till data man vill komma åt men som man inte äger. För ägande pekare använder man std::unique_ptr eller ibland std::shared_ptr om man har flera ägare. Man behöver alltså i stort sett aldrig anropa delete själv i modern C++.
Nej, jag menar bara när du använder någon klass från QT, t.ex. QChart, som tar över ägandeskapet av pekare du skickar in via dess metoder.

Jag är ny inom C++, trots att jag började med C++ för 15 år sedan, men jag har kört annat mellan där. C++98 är det enda som jag kommer ihåg. Känner inte igen "nakna" pekare eller "smarta" pekar. Notera att jag programmerar mycket C då det är mer lämpat mot industri, framförallt automation och hårdvara.

Jag tror jag gör så här.
Jag deklarerar i konstruktören, och sedan får jag acceptera att jag behöver inte deklarera nytt.

void Logging_tab::create_series(){ for(uint8_t i = 0; i < 8; i++) pwm_series[i] = new QLineSeries();

Permalänk
Hedersmedlem
Skrivet av heretic16:

Jag tror jag gör så här.
Jag deklarerar i konstruktören, och sedan får jag acceptera att jag behöver inte deklarera nytt.

void Logging_tab::create_series(){ for(uint8_t i = 0; i < 8; i++) pwm_series[i] = new QLineSeries();

Om du kör removeSeries först får du som sagt radera den om du vill, fast det är kanske effektivare att återanvända den om det går.