Wkaźnik na usunięty element listy – na co będzie wskazywał?

0

Na co będzie wskazywał wskaźnik wsk?

std::deque<int> data;
data = {0,1,2,3,4,5};

int* wsk = &data[4];
data.erase(data.begin()+3));
6

Na miejsce, w którym wcześniej znajdowały się usunięte dane. Czyli w dalszym ciągu na jakieś dane – być może na to co zostało usunięte, jeśli nie zostało nadpisane (lub wyzerowane) lub na randomowe śmieci.

0

Czyli dobrze rozumiem, że element 5. i 6. zostaną pod swoimi starymi adresami?
A jeżeli chciałbym aby po usunięciu elementu z kontenera wskaźnik dalej wskazywał na adres z wartością 4, to powinienem użyć shared_ptr ?

std::deque<std::shared_ptr<int>> data;
data = {0,1,2,3,4,5};
 
std::shared_ptr<int> wsk = data[4];
data.erase(data.begin()+3));
1

shared_ptr to ostateczność. Możesz użyć wektora/deque unique_ptr, albo np. std::list (też raczej ostateczność)

3
mlp99 napisał(a):

Czyli dobrze rozumiem, że element 5. i 6. zostaną pod swoimi starymi adresami?
A jeżeli chciałbym aby po usunięciu elementu z kontenera wskaźnik dalej wskazywał na adres z wartością 4, to powinienem użyć shared_ptr ?

"All iterators and references are invalidated, unless the erased elements are at the end or the beginning of the container, in which case only the iterators and references to the erased elements are invalidated." https://en.cppreference.com/w/cpp/container/deque/erase

mlp99 napisał(a):

A jeżeli chciałbym aby po usunięciu elementu z kontenera wskaźnik dalej wskazywał na adres z wartością 4, to powinienem użyć shared_ptr ?

Próbujesz zrobić coś bardzo dziwnego. Nie rób tak, bo zrobisz sobie krzywdę nawet nie wiesz kiedy.

std::deque<int> data;
data = {0,1,2,3,4,5};
 
int value = data[4];
data.erase(data.begin()+3));
0

Sprawa z pamięcią wygląda tak - wszystko co jest na stosie to tam pozostaje nadal, póki nie zostaje nadpisane. np wywołaniem funkcji czy definicją nowych zmiennych. Przykład:

char *pointer, z;
for (int cnt = 0, cnt < 10, cnt++)//ESP + sizeof(int)
{
    char w = 'X';//ESP + sizeof(int) o ile dobrze pamiętam wskaźnik stosu jest przestawiany o całe słowo
    pointer = &w;
}
//ESP - 2 word tak mi się przynajmniej wydawało, ale jak działanie programu pokazuje chyba tak się nie dzieje.
//tu wprawdzie nie ma dostępu do cnt i w poprzez nazwę, ale na stosie nadal istnieją, więc na upartego dałoby radę się do nich dobrać używając asemblera, albo po bożemu zadeklarowanym wcześniej wskaźnikiem
z = *pointer;
cout << z;

int a = 12345678, b = 9987677, c = 1029384756;
function();//to powoduje odłożenie adresu powrotnego i parametrów na stos, ESP + word + z grubsza X*word za każdy parametr typu prostego. spowoduje to nadpisanie pamięci w której rezydował char w.
//ciekawe, w funkcji jednak też pokazuje 'X' - może to się robi wywołanie inline?
//zbadam to dokładniej wieczorem

https://onlinegdb.com/rk5YEA7kE

Jeśli chodzi o pamięć przyznawaną operatorem new to ją się dostaje ze sterty, czyli zasobów systemu operacyjnego. Jeśli taką pamięć się odda OSowi używając delete, to dostęp do niej wraca z powrotem pod kontrolę i monitoring OSa. Zapisane w oddanych komórkach pamięci dane raz, że nie wiadomo czy nadal istnieją, to poważniejszą konsekwencją jest wyjątek SIGSEGV jeśli spróbuje się do nich odwołać po tym, jak je oddała do puli pamięci OSa.

Jaki z tego wniosek - ZA SWAROGA, PERUNA, WELESA I TRYGŁAWA NIGDY TAK NIE RÓB!
(chyba, że jesteś wymiatającym programistą systemów operacyjnych i dokładnie wiesz, co robisz)

0
mlp99 napisał(a):

Czyli dobrze rozumiem, że element 5. i 6. zostaną pod swoimi starymi adresami?
A jeżeli chciałbym aby po usunięciu elementu z kontenera wskaźnik dalej wskazywał na adres z wartością 4, to powinienem użyć shared_ptr ?

std::deque<std::shared_ptr<int>> data;
data = {0,1,2,3,4,5};
 
std::shared_ptr<int> wsk = data[4];
data.erase(data.begin()+3));

NIE. Za zarządzenie pamięcią w tym miejscu odpowiada data która jest typu std::deque<std::shared_ptr<int>>. Wciskając tam shared_ptr skończy się na undefined behavior bo nagle dwa niepowiązane ze sobą obiekty odpowiadają za czas życia tego samego fragmentu pamięci.

Właściwe pytanie brzmi co chcesz osiągnąć?

0

Dziękuję wam za wyjaśnienie. I może jednocześnie faktycznie napiszę co chcę osiągnąć.

ma strukturę

struct MyStruct {
int i;
MyStruct* pointer
};

Oraz wektor (albo inny kontener) struktur:

std::vector<MyStruct> data;

Inicjalizuję strukturę:

data.resize(4);
data[0] = {0, &data[2]};
data[1] = {0, &data[3]};
data[2] = {0, &data[0]};
data[3] = {0, &data[1]};

Następnie tworzę drugi wektor, do którego chcę włożyć niektóre elementy z wektora pierwszego. Np element 2,3 ale tak, aby nadal wszystkie elementy wskazywały tak jak wskazują. A z wektora pierwszego elementy te usunąć. Pytanie czy można w jakiś sposób to osiągnąć? Coś na wzór:

std::vector<MyStruct> data2;
data2.push_back(data[1]);
data2.push_back(data[2]);

data.erase(std::find(data.begin(), data.end(), data[1]);
data.erase(std::find(data.begin(), data.end(), data[2]);

Chociaż jak to piszę to wydaje mi się, że strasznie kombinuję, a jedyne co chcę osiągnąć to rozdzielić kontener data, na kontener kontenerów, np wektor wektorów.

0

Przepraszam że nie w komentarzu ale potrzebuję fragmentu kodu..

Co do pytania o cel takiego działania, zgadzam się. Jest to "zagadkowe". Czy jednak tam będzie UB jeśli data wykonuje potencjalne delete na shared_ptr<int>? Przecież jawnie wykonując kopię tego wskaźnika zwiększam jego licznik dowiązań i tyle. W tym przypadku nie widzę tu UB... Chyba że chodzi Ci o .erase(...)? Ale i tu będzie ten sam mechanizm.

#include <memory>
#include <deque>
#include <iostream>

int main() {
    std::deque<std::shared_ptr<int>> data;
    // Takie "prostackie" zasilenie kontenera... 
    for (auto v: {0, 1, 2, 3, 4, 5}) {
        data.emplace_front(std::make_shared<int>(v));
    }

    std::shared_ptr<int> wsk = data[4];
    std::cout << "Before erase: " << wsk.use_count() << '\n';
    data.erase(data.begin() + 4);
    std::cout << "After erase: " << wsk.use_count() << '\n';
}
0

usuwając z wektora elementy powodujesz, że elementy po nim następujące zostają przesunięte.
Efekt jest taki, że MyStruct::pointer wskazuje na ten sam fragment pamięci, ale tam teraz może siedzieć coś innego!

Nadal opisujesz swój sposób rozwiązania problemu, a nie sam problem, który próbujesz rozwiązać!

0

Mam trzy wektory. Pierwszy określający obiekty w położeniu początkowym, drugi w końcowym, oraz trzeci jako wartość elementu.

std::vector<unsigned int> initialPos;
std::vector<unsigned int> finalPos;
std::vector<unsigned int>value;

np:

initialPos = {1, 2, 3, 4, 5};
finalPos = {3, 1, 2, 5, 4};
value = {10, 20, 30, 40, 50};

Powyższe dane muszę rozdzielić w taki sposób, aby oddzielić od siebie poszczególne cykle. W cyklu są te elementy które tworzą obieg zamknięty. Dla powyższych dancyh
cykl1: 1->3->2->1
cykl2 4->5->4

Stąd mój pomysł aby stworzyć strukurę obiektów

struct MyStruct {
int value;
MyStruct* pointer 
};

i wektor struktur

std::vector<MyStruct> data;

gdzie numeracja elementów w wektorze określa dane z initialPos, a MyStruct* pointer wskazuje na elementy zgodnie z finalPos

0

To brzmi jak materiał na listę cykliczną...niemniej, potrzeba dalszych doprecyzowań - czym dokładnie jest u ciebie jest obiekt, czym element, i czym wartość, oraz czym jest cykl.

0

Jest to zadanie edukacyjne. Mam słonie, które stoją w szeregu, initialPos określa ich kolejność początkową, a finalPos kolejność końcową. Zadanie sprowadza się do obliczenia minimalnego wysiłku potrzebnego do zmiany kolejności słoni. Wartości value, to masy słoni, a wysiłek związany z zamianą dwóch słoni miejscami okreslony jest wzorem masa1+masa2 czyli value1+valu2. Generalnie mam algorytmy do obliczenia minimalnego wysiłku. Oba jednak wymagają tego, aby analizować dane w ramach cykli. Cyklem nazywam taki zbiór słoni, które wzajemnie zamieniają się miejscami tworząc układ zamknięty.
Np.: słoń 1. musi przejść na miejsce słonia 2., słoń 2. na miejsce 3., a 3. na miejsce 1.
Innym cyklem może być: słoń 4. na miejsce 5, a 5. na miejsce 4.
Albo taki: słoń 6. zostaje na swoim miejscu.

0

Mhm...czyli cały zwierzyniec wygląda tak:
Słoń 1, Słoń 2, Słoń 3, Słoń 4, Słoń 5, Słoń 6
czy tych szeregów jest kilka:
Słonie 1..3
Słonie 4..5
Słoń 6
?

EDIT
Czyli algorytm przemieszczenia słoni wygląda tak:
jeśli indeks słonia do przestawienia < indeks docelowy
-> przesuń wszystkie słonie o indeksach od start + 1 do end o 1 w lewo
-> wstaw słonia który był pod start pod end
zgadza się, czy są jakieś dodatkowe warunki?

0

Nie, to jest inaczej. Ustawienia słoni mogą być dowolne. Podaję tylko przykłady. Może źle tłumaczę, postaram się na wykresie:

Nr słonia | 1|2|3|4|5|6|
-|-|-|-|-|
Pozycja początkowa słonia w rzędzie | 1|4|5|3|6|2|
Pozycja końcowa słonia w rzędzie |5|3|2|4|6|1|

0

czyli startowo jest: 1, 6, 4. 2, 3, 5 (na odpowiednich indeksach)
końcowo jest: 6, 2, 3, 4, 1, 5
i w czym tkwi problem że nie możesz słonia 1 przenieść z indeksu 1 od razu na 5?

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