Czy kompilator w ramach optymalizacji może przenieść obiekt bez wiedzy użytkownika?

0

Hejka,
dziwny error ostatnio miałem: po zamknięciu apki rzucało mi wyjątek read access violation i w 99% było to od tego, że destruktor inteligentnego wskaźnika próbował zniszczyć już nieistniejący obiekt. Zamiana na raw pointery rozwiązała problem i się zacząłem zastanawiać czy kompilator w ramach sztuczek optymalizacyjnych może przenosić jak ma możliwość obiekty bez wiedzy programisty. Więc jak jest?

Z przyzwyczajenia używam smart pointerów, ale chyba za dużo zachodu z tym w Qt5.
Pozdro :P

1

Nie może jeśli jest to zauważalne. Pewnie masz gdzieś double delete.

0

QObject + smart pointer = zło.

0

@kq: Co masz na myśli, że jeśli jest to zauważalne?

1

https://en.cppreference.com/w/cpp/language/as_if - jak kompilator gwarantuje, że nie zauważysz, że coś zrobił, to może to zrobić (no bo to tak jakby tego nie zrobił). Kompilator może założyć, że nie doprowadzisz do UB.

tajny_agent: niekoniecznie. Często jest to zbędne, jeśli korzystasz z quasi-gc w Qt, ale jak najbardziej możesz korzystać z unique pointerów jako elementów klas lub w ciałach funkcji.

2

Istnieją bugi w kompilatorach, ale na 99% możesz założyć że błąd był jednak w twoim kodzie a nie w optymalizacji ;) Pewnie zrobiłeś jakieś kasyczne double-free, albo user-after-free przypadkiem. Mógłbyś pokazać minimalny przykład który prezentuje twój problem.

0

Ooo bardzo ciekawa zasada. Co do smart pointerów, to były one prywatnymi zmiennymi w mojej klasie i inicjalizowałem je listą inicjalizacyjną przekazując do nich wskaźniki z ui. Mam dwie klasy do których przekazuję ten sam wskaźnik widżeta i ze smart pointerami rzucało mi ten wyjątek. Ze surowymi już nie, będę musiał później podpatrzeć co się dzieje pod spodem.

4

Dawno nie dotykałem Qt, ale była chyba tam taka zależność, że rodzic typu QObject niszczy swoje dzieci typu QObject jeśli te dzieci mają jawnie ustawionego rodzica. Czyli np. mając taki kod

class MyQObjectType : public QObject
{
public:
   MyQObjectType()
   {
       child1 = new QObject(this);
       child2 = new QObject();
       child2->setParent(this);
   }
private:
   QObject *child1;
   QObject *child2;

};

Jeśli zniszczysz instancję MyQObjectType to child1 i child2 zostaną również zniszczone. Także jeśli np. trzymałbyś child1 lub child2 w smart pointerze, będziesz miał double free.

Ogólnie to w Qt chyba wszystko dziedziczy po QObject, także jeśli masz formatkę z guzikami, to nie musisz ręcznie usuwać guzików, ani trzymać ich w smart pointerze, zasoby zaalokowane dla guzików zostaną automatycznie zwolnione, gdy zostanie usunięta formatka.

0
kq napisał(a):

tajny_agent: niekoniecznie. Często jest to zbędne, jeśli korzystasz z quasi-gc w Qt, ale jak najbardziej możesz korzystać z unique pointerów jako elementów klas lub w ciałach funkcji.

Ponoć można się nawet obejść bez new :P
Nie cierpię GC i na samą myśl o tym dostaje wysypki, ale dla własnego zdrowia wolę nie mieszać smart pointerów z wszelakimi Q... bo będzie jeszcze gorzej :)

0

Kolejny przykład typowy dla C++, że frameworki dają swój "lepszy" string, kontenery itd... i rzekomo to jest postęp.

1

W przypadku Qt jest to bardzo zrozumiałe, jako że Qt jest starsze niż ustandaryzowany C++, a np. QString jest o niebo przyjaźniejszy w użyciu niż std::string.

Tak czy inaczej, smart pointery mogą sparzyć jeśli usuwane są po tym, jak QObject je zwolni, przy czym są też wtedy zbędne - musisz wiedzieć kto jest odpowiedzialny za dany widżet. W przypadku GUI z reguły jest to klasa, która dany widżet posiada (patrz na "takes ownership" w dokumentacji). Niemniej jednak, jeśli usuniesz za pomocą delete (lub pośrednio, za pomocą smart pointera), to taki obiekt poprawnie się wyrejestruje i nie będzie problemu:

// ok
auto x = new QPushButton(widget);
delete x;
delete widget;
// UB
auto x = new QPushButton(widget);
delete widget; // (delete x w destruktorze widget)
delete x; // double delete
0

Okej, to rzeczywiście w klasach dziedziczących po QObject nie ma co korzystać z smart-pointerów. Niemniej jeśli klasa nie dziedziczy to czemu nie, ale zastanawiam się czy takie mieszanie jest w porządku :P Dziękuję za pomoc.

3

Jeśli wejdziesz między wrony, musisz krakać jak i one

Jak jesteś w środowisku Qt, to piszesz w stylu Qt (czyli używasz Q-rzeczy, może z wyjątkiem kontenerów, bo te są mocno api-kompatybilne z biblioteką standardową). W Qt6 czeka nas trochę zmian w tym zakresie, i Qt będzie bardziej "c++"-like, ale to jeszcze trochę czasu przed nami.

1

Dla obiektów nie dziedziczących z QObject nie widzę przeciwwskazań w używaniu smart pointerów.
W wypadku kiedy używamy wskaźnika do obiektu, którego nie jesteśmy właścicielem i który może zostać zniszczony w dowolnym momencie, zalecane jest używanie QPointer<T>, gdzie T jest typem obiektu dziedziczącego z QObject. W przypadku kiedy wskazywany obiekt zostaje usunięty, wskaźnik QPointer jest ustawiany na nullptr, unikając problemu "dangling pointers".

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