Problemy z unique_ptr i metodami.

1

Witam wszystkich jestem nowy na forum. Mam problem z unique_ptr albo z IDE.
Używam Code::Blocks 20.03 z Kompilatory/x86_64-13.1.0-release-posix-seh-ucrt-rt_v11-rev1/mingw64/ i MVS 2022 z
MSVC 19.38.33141 z PlatformToolset v143. Problem polega na tym że MVS po przeniesieniu funkcją move() własności na nowy unique_ptr mogę odwoływać się unieważnionym wskaźnikiem do metod class pomimo że stary unique_ptr zmienia się w nullptr .
Co do pól w class działa prawidłowo i kompilator zgłasza błędy. Jeszcze gorzej jest z Code::Blocks i gcc tam nie tylko mogę odwoływać się do metod ale i pól class i kompilator mówi że jest git i zero błędów. Ale w tym przypadku (co do zmiennych ) podpala się Norton i mówi bast! A metody przepuszcza.
Co jest nie tak? Kod programu:

 #include <iostream>
#include <memory>

class MyClass {
public:
    int x = 88;
    MyClass() { std::cout << "MyClass Constructor\n" << std::endl; }
    ~MyClass() { std::cout << std::endl << "MyClass Destructor" << std::endl; }

    void display() { std::cout << "Hello from MyClass\n" << std::endl; }
};

int main() {


        std::unique_ptr<MyClass> ptr1 = std::make_unique<MyClass>();

        std::cout << "ptr1->x  = " << ptr1 -> x << std::endl;
        std::cout << "ptr1->display() : ";
        ptr1->display();

        std::unique_ptr<MyClass> ptr2 = std::move(ptr1);

        std::cout << "ptr2 = std::move(ptr1)" << std::endl << std::endl;

        std::cout << "ptr2->x = ";
        std::cout << ptr2->x << std::endl;
        std::cout << "ptr2->display() : ";
        ptr2->display();

        if (ptr1 == nullptr) {
            std::cout << "Now ptr1 is nullptr after \"ptr2 = move(ptr1)\"!" << std::endl;
            std::cout << "Whay does it work: ptr1->display()!!!!!!!!!! : " ;
            ptr1->display();
        }
        //brak błędu i to działa 
        ptr1->display();
        std::cout << "\n";

        //std::cout << ptr1 -> x << std::endl; //Tutaj ok przy zmiennych kompilator zgłasza błąd.

    return 0;
}
5

Wywołujesz UB, więc każde zachowanie jest poprawne. Zauważ, że w metodzie display() nie odnosisz się do żadnego elementu klasy, więc każde zachowanie jest "poprawne". Jeśli chcesz diagnozować takie przypadki użyj jednego z sanitizerów, np. AddressSanitizera lub UBSanitizera.

0

Czyli dodać flagę w gcc : -fsanitize=undefined ?

0

Ok, teraz wychwytuje błędy, ale aż 50 nawet jak usunę odwołania do unieważnionych wskaźników. Muszę się przypatrzyć tym sanitizerom bo to dla mnie nowość.

0

Ok, odniesienie się przez display() do elementu klasy daje efekt i wszystko działa jak należy. Szkoda że kompilator sam nie wychwytuje takiego błędu z metodami tylko jeszcze jedna rzecz na głowie programisty. Przerobiłem class dodając stringa z którego korzysta metoda display() i jest ok.

class MyClass {
public:
int x = 88;
std::string y = "Hello from MyClass! ";
MyClass() { std::cout << "MyClass Constructor" << std::endl; }
~MyClass() { std::cout << "MyClass Destructor" << std::endl; }
void display() { std::cout << y << std::endl; }
};

2

klasyk
https://cppcheck.sourceforge.io/
https://clang.llvm.org/extra/clang-tidy/

Od razu na tym kodzie wyrzuci ci analiza błedy.

2
_Adam napisał(a):

Szkoda że kompilator sam nie wychwytuje takiego błędu z metodami tylko jeszcze jedna rzecz na głowie programisty.

No niestety tak język C++ został zaprojektowany, że wychwycenie takich błędów nie jest możliwe. Bo co jakbyś niedeterministycznie przenosił albo nie? Np. o tak:

        std::unique_ptr<MyClass> ptr1 = std::make_unique<MyClass>();

        if (rand() == 0) {
                std::unique_ptr<MyClass> ptr2 = std::move(ptr1);
        }
        ptr1->display();
0
enedil napisał(a):
_Adam napisał(a):

Szkoda że kompilator sam nie wychwytuje takiego błędu z metodami tylko jeszcze jedna rzecz na głowie programisty.

No niestety tak język C++ został zaprojektowany, że wychwycenie takich błędów nie jest możliwe. Bo co jakbyś niedeterministycznie przenosił albo nie? Np. o tak:

        std::unique_ptr<MyClass> ptr1 = std::make_unique<MyClass>();

        if (rand() == 0) {
                std::unique_ptr<MyClass> ptr2 = std::move(ptr1);
        }
        ptr1->display();

No tak na warunkowe kasztany w kodzie nie ma rady i zostaje tylko mielenie testów. Ale poza warunkowym użyciem unique_ptr kompilator mógłby się bardziej postarać a tak pozostaje dłuższy kod z sprawdzeniem czy to czasem nie nullptr lub jakieś narzędzia jak kolega wyżej podał.

0

"jak nie ma, jak ci pokazałem wyżej że są. cppcheck dosłownie wali warnigniem w tych miejscach. "4p.cpp:37:9: warning: Access of moved variable 'ptr1'. [accessMoved] "
Wspomniałem o tym na końcu postu w "lub jakieś narzędzia jak kolega wyżej podał.".
Mam problem z dodaniem tego cppcheck do Code::Blocks ale wieże na słowo że działa, muszę tylko pokombinować jeszcze trochę z ty cppcheck.

0

Ok, uruchomiłem z wiersza poleceń cppcheck i działa bardzo dobrze, daje ostrzeżenia "Access of move variable" i "Null pointer dereference".
Sprawdziłem też przykład który podał kolega "enedil" z warunkowym przeniesieniem w if własności za pomocą move() i też cppcheck dobrze wychwycił i dał ostrzeżenie "Access of move variable". Dziękuję panowie za rozwiązanie problemu.

0

Nie czytałem całej dyskusji.

std::cout << "Now ptr1 is nullptr after "ptr2 = move(ptr1)"!" << std::endl;

No nie, ptr1 będzie w stanie "moved from" (ja to tak nazywam) czyli w stanie niezdefiniowanym. Chcesz tam mieć nullptr to musisz jawnie przypisać.

0
https://godbolt.org/z/qeh67qn81

Ten SonarClouds podlinkowany przez MarkaR22 robi dobre wrażeni i jest darmowa wersja, do nauki będzie dobry.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.