Qt/C++ Dlaczego w klasie wskaźnik nie przyjmuje adresu

1

Chciałbym prosić o wyjaśnienie - bo szczerze mówiąc nie rozumiem.

Chciałbym zaznaczyć, że do tej pory myślałem, że dowolna zmienna prywatna znajdująca się w klasie gdy choć JEDEN raz zostanie zainicjalizowana przez składową funkcję tej klasy, to będzie trzymać tą wartość dopóki nie zostanie drugi raz zainicjalizowana inną wartością przez tą samą lub inną funkcję składową tejże klasy.

Chciałbym rozpatrzyć jeden problem z którym spotykam się obecnie i nie rozumiem czemu tak się dzieje, chciałbym aby rzeczywiście i rozumnie ktoś z was mi to wyjaśnił

jest sobie prosta klasa:

class Klasa1
{
public:
    void CreateSettings(QJsonObject &object);
    void Settings();

private:
    QJsonObject *createSettings;
};

potem mam dwie funkcje tej klasy

void Klasa1::CreateSettings(QJsonObject &object)
{
//w tej funkcji mogę działać na tym wskaźniku, obiekt poprawnie odebrał adres i mogę wykonać jakieś działania na obiekcie

    this->createSettings = &object;
    this->createSettings->value("key", "anyValue").toString();
}

oraz druga funkcja

void Klasa1::Settings()
{
//w tej funkcji chcę wykorzystać wskaźnik który wyżej został zainicjalizowany adresem który otrzymała powyższa funkcja
//ale niestety ku mojemu zdziwieniu ten wskaźnik jest pusty - nie ma adresu obiektu który odebrałem w funkcji powyżej - DLACZEGO ?

    this->createSettings->value("key", "anyValue").toString();
}

Naprawdę proszę o wyjaśnienie.

1

MCVE!
https://dsp.krzaq.cc/post/445/jak-zadawac-pytania-na-forum/

Strzelam, że nie rozróżniasz klasy od obiektu.

0

@kq:

Strzelam, że nie rozróżniasz klasy od obiektu.

czemu ? z książek wiem, że obiekt jest jakiejś klasy, czyli tak naprawdę obiektem można nazwać wszystko np

int i;

i jest obiektem typu int... jeżeli się mylę, to trza poprawić autorów ksiażek

1

Strzelam, bo jest za mało danych. Przygotuj MCVE

3

@zkubinski: Ja myślałem że działa prawo zachowania masy i zamknięta butelka wody zawsze waży tyle samo, czemu jej masa wzrasta?
Jak opowiadasz o cudach niewidach to przedstaw jakikolwiek dowód w postaci kodu który każdy może sobie skompilować.
I rzuć ten cholerny Qt, za wcześnie ci w to się bawić, zacznij od prostych rzeczy.

0

@kq:

chciałeś kod

klasa "AllSettings"

te dwie funkcje - pierwsza działa, druga ni uja i nie wiem dlaczego, a powinno...

void AllSettings::CreateSettings(QJsonObject &createSettings)
{
    this->createSettings = &createSettings;

    if(createSettings.contains("ipaddr")){
        qDebug()<< "CreateSettings" << createSettings.value("ipaddr").toString();
    }
}

void AllSettings::CrSettings()
{
    qDebug()<< "CrSettings" << createSettings->keys();
}

Druga funkcja zadziała po wciśnięciu przycisku "zapisz"

0

@enedil:

no dobrze, i skąd wiesz, że CrSettings jest odpalane dopiero po CreateSettings?

tym pytaniem mnie zagiąłeś ALE:

  1. w klasie "mainWindow" mam connecta
QObject::connect(MyPage1, &Page1::ipAddr, &mySettings, &AllSettings::CreateSettings);

więc cokolwiek wpiszę w pole, to connect działa i pokazuje mi co wpisuję

  1. gdy uznam, że pole jest wpisane odpowiednią wartością, to chcę to "zapisać", a raczej zasymulować zapis, klikając przycisk "zapisz" po to aby zobaczyć czy mogę przesłać wskaźnik do zawartości gdzieś dalej np do innej klasy
QObject::connect(PbSave, &QPushButton::clicked, &mySettings, &AllSettings::CrSettings);

nic tam nie mam, przynajmniej mi nic nie pokazuje - w jaki sposób to wnioskujesz? bo lista kluczy jest pusta? no ale to nie o listę kluczy się ponoć rozchodziło, tylko w ogóle o zainicjalizowanie pointera

jak zainicjalizuję wskaźnik, to powinienem móc się odwołać do danych tego obiektu, a nie mogę i nie wiem czemu...

0

@zkubinski: Czekaj, bo ja nadal nie rozumiem. Crashuje się program, czy nie?

2
zkubinski napisał(a):

Chciałbym prosić o wyjaśnienie - bo szczerze mówiąc nie rozumiem.

Chciałbym zaznaczyć, że do tej pory myślałem, że dowolna zmienna prywatna znajdująca się w klasie gdy choć JEDEN raz zostanie zainicjalizowana przez składową funkcję tej klasy, to będzie trzymać tą wartość dopóki nie zostanie drugi raz zainicjalizowana inną wartością przez tą samą lub inną funkcję składową tejże klasy.

W powszechnego rozumienia INICJOWAĆ można tylko raz, potem to jest podstawienie.
Jak ktoś ma słusznie obawy, że czasem panie głupotę, używa się const żeby twoją "drugą inicjację" wychwycić jako błąd.
Pole const można zainicjować (nawet tzreba, w konstruktorze), nie można pod nie podstawić.

w 200% zgodzę się z Koelgami, zostaw to Qt w choelrę, aż opanujesz język z dobrych źródeł, a nie z własnego "myślałem że"

0

@AnyKtokolwiek:

w 200% zgodzę się z Koelgami, zostaw to Qt w choelrę, aż opanujesz język z dobrych źródeł, a nie z własnego "myślałem że"

nie zgodzę się z tym, aplikacje konsolowe są proste i wszystko działa, bo jest jedna funkcja main która wszystko razem odpali na dzień dobry, tylko gdy piszę aplikację GUI, to sposób myślenia się zmienia, tutaj następuje interakcja z użytkownikiem i ten sam kod trzeba już zapisać zupełnie inaczej i bardzo wiele elementów się zmienia

4

Masz funkcję

void AllSettings::CreateSettings(QJsonObject &createSettings)
{
    this->createSettings = &createSettings;

    if(createSettings.contains("ipaddr")){
        qDebug()<< "CreateSettings" << createSettings.value("ipaddr").toString();
    }
}

która jest slotem dla sygnału

QObject::connect(MyPage1, &Page1::ipAddr, &mySettings, &AllSettings::CreateSettings);

sygnał emitujesz w funkcji

void Page1::setIPAdress(const QString &ipAddress)
{
    QJsonObject ip;

    ip["ipaddr"] = ipAddress;

//    qDebug()<< ip.value("ipaddr").toString();

    emit ipAddr(ip);
}

Widzisz problem?
W AllSettings::createSettings zapisujesz we wskaźniku adres lokalnej zmiennej z funkcji Page1::setIPAdress. Ta zmienna kończy swój żywot w momencie wyjścia z funkcji, więc wskaźnik trzyma adres pod którym tego QJsonObject już dawno nie ma.

7

Twoje MCVE niezbyt kompiluje się na linuksie, bo masz niepoprawne \.

W Page1 po każdej zmianie IP emitujesz lokalną zmienną ip przez referencję.

void Page1::setIPAdress(const QString &ipAddress)
{
    QJsonObject ip;

    ip["ipaddr"] = ipAddress;

//    qDebug()<< ip.value("ipaddr").toString();

    emit ipAddr(ip);
}

Przekazujesz ją sygnałami i slotami:

    QObject::connect(MyPage1, &Page1::ipAddr, &mySettings, &AllSettings::CreateSettings);

do

void AllSettings::CreateSettings(QJsonObject &createSettings)
{
    this->createSettings = &createSettings;

    if(createSettings.contains("ipaddr")){
        qDebug()<< "CreateSettings" << createSettings.value("ipaddr").toString();
    }
}

Ponieważ działasz jednowątkowo to na razie jest to bezpieczne. Niestety, po wyjściu z funkcji obiekt ip, a zatem ten, na który wskazuje AllSettings::createSettings przestaje istnieć. Więc w momencie zapisu masz UB.

Poprawienie tego rozwiązuje crasha, ale zapis nadal nie działa. Możesz przekazywać dane do właściwej klasy:

	QObject::connect(MyPage1, &Page1::ipAddr, &mySettingsFile, &SettingsFile::updateSettings);
	void updateSettings(QJsonObject const&);
void SettingsFile::updateSettings(const QJsonObject& obj)
{
	JsonObject = obj;
}

I teraz działa:

% cat ../build-QStackedWidget-v2-Desktop-Debug/settings.json
{
    "ipaddr": "123131"
}

Przy czym ogólnie widać że nie masz planu co gdzie przekazujesz, trzymasz obiekty w klasach które nie powinny w nich być i dzielisz rzeczy które powinny być razem na oddzielne klasy.

Dodatkowo: pliki .user Qt Creatora są lokalne i nie powinno być ich w repo.

Puściłem merge requesta.

0

@tajny_agent: & @kq:

DZIĘKUJĘ WAM za odpowiedź, fajnie, że po tylu latach otrzymałem konkretną pomoc popartą przykładem i wyczerpującą odpowiedzią !!!

0

już to kiedyś ci pisałem weź sobie sprawdzaj kod tym też
https://github.com/KDE/clazy
https://clang.llvm.org/extra/clang-tidy/

0

@revcorey:

już to kiedyś ci pisałem

możliwe ale najbardziej dociera przykład i wyjaśnienie jak zrobili koledzy wyżej - to daje 200% więcej, a nawet 500% więcej do zrozumienia

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