funkcja zwraca wskażnik na QVector

0

Zrobiłem sobie taki kod w ramach treningu.

    void *Dane(){
    void *wskNaPojemnik;

    QVector<QString> pojemnik{"tekst1", "tekst2"};

    qDebug()<<pojemnik[0] << pojemnik[1];

    wskNaPojemnik = static_cast<void*>(&pojemnik[0]);

    qDebug()<<"adres" << wskNaPojemnik;

    return wskNaPojemnik;
}

int main(void)
{
    QVector<QString> *wskStr;
    QVector<QString> odbior;
    QVector<QString>::Iterator it;

    wskStr = static_cast<QVector<QString>*>(Dane());

    odbior = *wskStr;

    qDebug()<<odbior;

    for(it=odbior.begin(); it<odbior.end(); ++it){
        qDebug()<< *it;
    }

    return 0;
}

teraz mam pytanie. Jak w funkcji main mogę się dostać do dowolnego elementu QVectora którego otrzymałem adres ?

0

Dostałeś adres pierwszego elementu wektora. To nie jest to samo co adres wektora.
Drugim problemem jest to, że zwracasz wskaźnik na dane lokalne funkcji. Próba odczytania ich po wyjściu z funkcji to UB.

0

więc jak to odczytać prawidłowo ?

0

dzięki za podpowiedź o danych lokalnych. Już sobie poradziłem. Zastanawiam się dlaczego QVector musi być zadeklarowany globalnie ?

static QVector<QString> pojemnik{"test1", "test2", "test3"};

void *Dane(){
    void *wskNaPojemnik;

//    QVector<QString> pojemnik{"test1", "test2", "test3"};
    QString *wskStr;

    wskStr = pojemnik.data();

    for(int i=0; i<pojemnik.size(); ++i){
        qDebug()<< wskStr[i];
    }

    qDebug()<<pojemnik.size() << pojemnik.isEmpty() << &pojemnik;

    wskNaPojemnik = static_cast<void*>(&pojemnik);

    qDebug()<<"adres" << wskNaPojemnik;

    return wskNaPojemnik;
}

int main(void)
{
    QVector<QString> *wskStr;
    QVector<QString> odbior;
    QVector<QString>::Iterator it;
    QString *wsk;

    wskStr = static_cast<QVector<QString>*>(Dane());

    odbior = *wskStr;
    wsk = odbior.data();

    for(int i=0; i<odbior.size(); ++i){
        qDebug()<< wsk[i];
    }

    return 0;
}

poniżej kod który działa na danych lokalnych funkcji dla dwóch przypadków

  1. string
using namespace std;

void *Dane(){
    void *wskNaPojemnik;

    string str="test1";

    wskNaPojemnik = static_cast<void*>(&str);

    return wskNaPojemnik;
}

int main(void)
{
    string *wsk, str;

    wsk = static_cast<string*>(Dane());

    str = wsk->data();

    cout<< str;

    std::cout << std::endl;

    return 0;
}
  1. int
void *Dane(){
    void *wskNaPojemnik;

    int zm=8;

    wskNaPojemnik = static_cast<void*>(&zm);

    return wskNaPojemnik;
}

int main(void)
{
    int *wsk;

    wsk = static_cast<int*>(Dane());

    int a;

    a = *wsk;

    qDebug()<< "odebrano" << a;

    return 0;
}


0

nie musi być deklarowany globalnie. Wystarczy w funkcji mu nadać przydomek static

void *Dane(){

       static QVector<QString> pojemnik;

       //treść funkcji

       return wskNaPojemnik;
}
0
zkubinski napisał(a):

poniżej kod który działa na danych lokalnych funkcji dla dwóch przypadków

  1. string

Chyba nie działa: https://onlinegdb.com/B1kCPCY1L

  1. int

Mały dodatek i ... ups: https://onlinegdb.com/SydQnCK1U

zkubinski napisał(a):

nie musi być deklarowany globalnie. Wystarczy w funkcji mu nadać przydomek static

Zgadza się. To sprawi że dane pozostaną w pamięci po wyjściu ze scope'a funkcji.
Możesz też:

  • dynamicznie przydzielić pamięć na dane (ale wtedy musisz zadbać o zwalnianie tej pamięci),
  • możesz przekazać (adres na) kontener jako parametr,
  • ...

Unikaj połączenia void * i cpp. To jak proszenie się o kłopoty.

0

ze stringiem pewnie tam nie działa, bo jest metoda data()

rozumiem, że lepiej unikać rzutowania na typ void * ale niestety muszę zrozumieć po co i dlaczego to się robi, ponieważ twórcy biblioteki Qt używają takiego rzutowania w takim kodzie jak przykład niżej. To jest mój model, który dziedziczy po klasie QAbstractItemModel, chodzi mi o metodę createIndex

QModelIndex MyModel::index(int row, int column, const QModelIndex &parent) const
{
    myDataText = static_cast<myText*>(parent.internalPointer());

    return createIndex(row,column,static_cast<void*>(myDataText));
}

owszem, nie działa mi powyższe ale próbuję zrozumieć jak używać tej funkcji - w sumie jest ich kilka ale te są najważniejsze, bo zwracają wskaźnik na dane w modelu ale póki co nie wiem jak ich użyć

    QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
    QModelIndex parent(const QModelIndex &index) const override;
0

To rzutowanie w górę. Aby go użyć powinieneś mieć pewność co do typu obiektu.

Podana funkcja zwraca wskaźnik do danych przekazanych w parametrze.

Delor napisał(a):

Możesz też:
...

  • możesz przekazać (adres na) kontener jako parametr,
0

@zkubinski: createIndex ma dwie wersje

QModelIndex	createIndex(int row, int column, void *ptr = nullptr) const
QModelIndex	createIndex(int row, int column, quintptr id) const

Wywołanie z rzutowaniem na void* służy do wymuszenia na kompilatorze, żeby użył pierwszej wersji i nie zwracał uwagi na inne przeładowania. Trzeci parametr w createIndex to jest user-data i służy do powiązania QModelIndex z jakąś własną pomocniczą strukturą. Na pewno nie jest to odpowiednie miejsce na przechowywanie danych, które będą zwracane przez funkcję QVariant data(QModelIndex const& index, int role)

zkubinski napisał(a):

owszem, nie działa mi powyższe ale próbuję zrozumieć jak używać tej funkcji - w sumie jest ich kilka ale te są najważniejsze, bo zwracają wskaźnik na dane w modelu ale póki co nie wiem jak ich użyć

    QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
    QModelIndex parent(const QModelIndex &index) const override;

Te funkcje nie zwracają wskaźnika tylko klasę/strukturę opisującą dany element w modelu.

0

Jak rozumiem cały model ?
Więc tak.

  1. Dane wczytuję w konstruktorze np z pliku.
  2. Po wczytaniu danych z pliku poniższe metody
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;

zwracają ile jest kolumn i wierszy w płaskim modelu danych (głownie będzie to macierz)

  1. metoda
Qt::ItemFlags flags(const QModelIndex &index) const override;

ustawia flagę która pozwala na edycję wybranych kolumn - domyślnie QTableView jest w trybie nieedytowalnym. I żeby to zmienić używa się tej metody

  1. Dla poniższych metod
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &index) const override;

moje domysły są takie -> w funkcji main wywołujemy te metody podając im odpowiednie argumenty aby wiedziały co mają robić aby zwrócić "klasę/strukturę" z odpowiednim indexem. Piszę tak, bo nie wiem niczego na ten temat..

  1. Po otrzymaniu właściwych indexów metoda
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;

zwraca dane do widoku i je w nim wyświetla. Można te dane sobie wypisać w jakiejś funkcji

  1. Natomiast metoda
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;

ustawia dane w modelu i te dane przekazuje również funkcji data aby ustawiła je w widoku. Nie wiem też, jak miałby wyglądać zrzut tych danych z powrotem do pliku

w załączeniu kod który wykombinowałem... owszem jakoś działa ale nie mogę tego wszystkiego spiąć w logiczną i chronologiczną całość.

Nie rozumiem też jak mogę tworzyć samemu indexy za pomocą QModelIndex

0
zkubinski napisał(a):
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;

zwracają ile jest kolumn i wierszy w płaskim modelu danych (głownie będzie to macierz)

Dla każdego modelu, nie tylko płaskiego. W przypadku modeli tabelarycznych dokumentacja radzi, żeby zwracać 0 jeśli parent jest poprawny.

  1. metoda
Qt::ItemFlags flags(const QModelIndex &index) const override;

ustawia flagę która pozwala na edycję wybranych kolumn - domyślnie QTableView jest w trybie nieedytowalnym. I żeby to zmienić używa się tej metody

Z tego co pamiętam to edycję można też wyłączyć z poziomu QTableView bez konieczności ustawiania tego na sztywno w modelu. Ale mogę się mylić.

  1. Dla poniższych metod
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &index) const override;

moje domysły są takie -> w funkcji main wywołujemy te metody podając im odpowiednie argumenty aby wiedziały co mają robić aby zwrócić "klasę/strukturę" z odpowiednim indexem. Piszę tak, bo nie wiem niczego na ten temat..

Zasadniczo to tych funkcji nie powinieneś w ogóle ręcznie wywoływać, bo poza komunikacją na linii model-widok nie ma to większego sensu.

  1. Po otrzymaniu właściwych indexów metoda
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;

zwraca dane do widoku i je w nim wyświetla. Można te dane sobie wypisać w jakiejś funkcji

  1. Natomiast metoda
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;

ustawia dane w modelu i te dane przekazuje również funkcji data aby ustawiła je w widoku. Nie wiem też, jak miałby wyglądać zrzut tych danych z powrotem do pliku

Jak wyżej. Te funkcje służą przed wszystkim do komunikacji model<->widok czy model<->proxy. Ja ich nigdy nie wywołuje bezpośrednio, tylko dodaje do modelu wrappery o bardziej konkretnych nazwach, bez zabawy w konwersję z/na QVariant czy przypominanie sobie z jaką role mam je wywołać.

Nie rozumiem też jak mogę tworzyć samemu indexy za pomocą QModelIndex

Pytanie, do czego Ci to potrzebne ;)

Żeby zobaczyć jak zaimplementować model od zera możesz spojrzeć tutaj.
Masz tam przykładową implementację zarówno funkcji index, jak i parent.

Jeśli model ma formę tabelaryczną to dziedzicząc po QAbstractTableModel w gratisie dostajesz domyślną implementację kilku metod jak np. index czy parent.
Ogółem trochę przekombinowałeś.
Pisane z palca, więc mogą się trafić jakieś przeoczenia ;)

class MyModel : public QAbstractTableModel
{
  Q_OBJECT

  public:
    explicit MyModel(QObject* parent = nullptr);
    int rowCount(const QModelIndex& parent = QModelIndex{}) const override;
    int columnCount(const QModelIndex& parent = QModelIndex{}) const override;
    Qt::ItemFlags flags(const QModelIndext& index) const override;
    QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
    bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;

  private:
    inline bool isValidIndex(const QModelIndex& index) const;
    inline int getStringIndex(int row, int column) const;
    static constexpr int numRows = 5;
    static constexpr int numCols = 3;
    QStringList texts;
};

implementacja

MyModel::MyModel(QObject* parent) : QAbstractTableModel(parent)
{
    //Przykładowe dane
    for (int r = 0; r < rowCount(); ++r) {
      for (int c = 0; c < columnCount(); ++c) {
        auto str = QString("row-%1, col-%2").arg(r).arg(c);
        texts.push_back(str);
      }
    }
  }

int MyModel::rowCount(const QModelIndex& parent) const
{
  // jeśli model ma formę tabelaryczną to zwraca liczbę wierszy tylko jeśli parent.isValid() == false
  return parent.isValid() ? 0 : numRows;
}

int MyModel::columnCount(const QModelIndex& parent) const
{
  // ta sama zasada co w rowCount
  return parent.isValid() ? 0 : numCols;
}

Qt::ItemFlags MyModel::flags(const QModelIndex& index) const
{
  auto flags = QAbstractTableModel::flags(index);
  // pozwalamy edytować wszystko oprócz pierwszej kolumny
  if (isValidIndex(index) && index.column() != 0)  
    flags |= Qt::ItemIsEditable;
  return flags;
}

QVariant MyModel::data(const QModelIndex& index, int role) const
{
  if (isValidIndex(index)) {
    if (role == Qt::DisplayRole) {
      int idx = getStringIndex(index.row(), index.column());
      return texts[idx];
    }
  }
  return {};
}

bool MyModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
  if (isValidIndex(index)) {
    if (role == Qt::EditRole) {
      int idx = getStringIndex(index.row(), index.column());
      texts[idx] = value.toString();
      return true;
    }
  }
  return QAbstractTableModel::setData(index, value, role);
}

int MyModel::getStringIndex(int row, int column) const
{
  return row * numCols + column;
}

bool MyModel::isValidIndex(const QModelIndex& index) const
{
  return index.isValid() && index.row() < numRows && index.column() < numCols;
}
0

Z tego co pamiętam to edycję można też wyłączyć z poziomu QTableView bez konieczności ustawiania tego na sztywno w modelu. Ale mogę się mylić.

tak, jest taka funkcja chyba to jest to -> void setEditTriggers(QAbstractItemView::EditTriggers triggers)

Pytanie, do czego Ci to potrzebne

żaby rozumieć model/widok, umieć dobrze programować i mieć większą kontrolę nad tym co chciałbym zrobić ;)

dzięki za odpowiedź, zobaczę co mi z tego wyjdzie

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