Hej,
pewnie dość proste i szybkie pytanie:
Stosuję vector'y obiektów w moim programie.
Jeśli chcę kolejno wpisywać określone wartości do zmiennych obiektów vectora - czy lepszym rozwiązaniem będzie zastosowanie iteratora czy może zwykłego int i w pętli np. for?
Które z rozwiązań jest szybsze, bardziej powiedziałbymprofesionalne i pożądane?
Pozdrawiam!
Teoretycznie po to są właśnie iteratory - są zoptymalizowane pod kątem dostępu sekwencyjnego. Stosując indeksowanie intem korzystasz z dostępu swobodnego (Random access), który może być wolniejszy, bo za każdym razem wymaga wyliczenia adresu obiektu względem początku, zamiast po prostu przesuwania się dalej. A już najładniej jest chyba skorzystać z range-loop:
for(auto& object : container)
{
object = ...
}
W praktyce nie ma to aż takiego znaczenia. Czasem musimy znać wartość indeksu w każdej iteracji, więc wtedy wygodniej jest indeksować.
czy może zwykłego int i w pętli np. for?
W zasadzie powinno się używać std::size_t
zamiast int
, bo taki typ zwraca metoda size().
GutekSan napisał(a):
Teoretycznie po to są właśnie iteratory - są zoptymalizowane pod kątem dostępu sekwencyjnego. Stosując indeksowanie intem korzystasz z dostępu swobodnego (Random access), który może być wolniejszy, bo za każdym razem wymaga wyliczenia adresu obiektu względem początku, zamiast po prostu przesuwania się dalej. A już najładniej jest chyba skorzystać z range-loop:
for(auto& object : container) { object = ... }
W praktyce nie ma to aż takiego znaczenia. Czasem musimy znać wartość indeksu w każdej iteracji, więc wtedy wygodniej jest indeksować.
Z tego co rozumiem, w przypadku chęci "indeksowania" nawet bardziej optymalnie jest wykorzystać "zwykłego fora", bo dla range-loop musiałbym użyć dodatkowej zmiennej np. auto it = std::begin(values); inkrementowanej w pętli.
YooSy napisał(a):
czy może zwykłego int i w pętli np. for?
W zasadzie powinno się używać
std::size_t
zamiastint
, bo taki typ zwraca metoda size().
Dokładnie tak, int podałem jako czysty przykład zmiennej.
Dzięki za odpowiedzi! :)
CzakuGim napisał(a):
Z tego co rozumiem, w przypadku chęci "indeksowania" nawet bardziej optymalnie jest wykorzystać "zwykłego fora", bo dla range-loop musiałbym użyć dodatkowej zmiennej np. auto it = std::begin(values); inkrementowanej w pętli.
Podałeś zły przykład. Dla range-loop musiałbyś mieć dodatkową zmienną, ale nie typu iteratora, jak powyżej, tylko właśnie int albo size_t, bo przecież tego potrzebujesz. Które z tych rozwiązań jest bardziej optymalne czasowo? Nie wiem, podejrzewam że i tak wciąż wersja z iteratorem i dodatkową zmienną. Może zależy od problemu.
Temat był przez kogoś analizowany na 4p, jak poszukasz to na pewno znajdziesz taką analizę (nie ma różnicy).
Na cpp-con Godbolt (autor Compiler Explorer) pokazywał, że dla kompilatora nie ma różnicy (kod wynikowy jest w zasadzie identyczny).
YooSy napisał(a):
czy może zwykłego int i w pętli np. for?
W zasadzie powinno się używać
std::size_t
zamiastint
, bo taki typ zwraca metoda size().
Nie. Metoda size
- o czym mówi zalinkowany przez Ciebie artykuł w cppreference - nie zwraca typu size_t
, tylko std::vector::size_type
, który najczęściej jest aliasem do size_t
, ale - o ile dobrze pamiętam - standard wcale nie wymaga, by był. (Pomijam template dla czytelności)
Masz rację.
std::size_t
zazwyczaj jest wystarczające, ale https://stackoverflow.com/questions/12498709/can-a-size-type-ever-be-larger-than-stdsize-t.
Lexik napisał(a):
robienie vektora objektow nie wydaje sie zbyt rozsadne. Lepiej smart pointery uzywac ;P
Chętnie przeczytam rozwinięcie :D Dlaczego lepiej?
Właśnie trochę poczytałem o smart pointerach, no ale to zwykle sama teoria.
Jeśli nie używasz polimorfizmu to nie widzę sensu w tworzeniu kontenera wskaźników zamiast kontenera obiektów.
tajny_agent napisał(a):
Jeśli nie używasz polimorfizmu to nie widzę sensu w tworzeniu kontenera wskaźników zamiast kontenera obiektów.
Jakie korzyści płyną z tworzenia kontenera wskaźników na obiekty a jakie z kontenera obiektów ?
Czemu uwarunkowane jest to polimorfizmem ?
Jakie korzyści płyną z tworzenia kontenera wskaźników na obiekty a jakie z kontenera obiektów ?
Czemu uwarunkowane jest to polimorfizmem ?
Wskaźnik daje dostęp do konkretnych klas pochodnych.
Jeśli nie korzystasz z polimorfizmu nie ma sensu tworzyć kontenera wskaźników na obiekty,
bo i tak muszą powstać w pamięci, aby korzystały z nich wskaźniki.
Po co dodawać pośredni sposób odwołania od obiektów skoro istnieje bezpośredni?
#include <iostream>
#include <vector>
#include <memory>
struct A {
virtual void Show() const { std::cout << __FUNCTION__ << '\n'; }
};
struct B : public A {
virtual void Show() const override { std::cout << __FUNCTION__ << '\n'; }
};
struct C : public A {
virtual void Show() const override { std::cout << __FUNCTION__ << '\n'; }
};
int main() {
std::vector<A> objects;
objects.push_back(B{});
objects.push_back(C{});
std::vector<std::unique_ptr<A>> ptrs;
ptrs.push_back(std::make_unique<B>());
ptrs.push_back(std::make_unique<C>());
std::cout << "Kontener objects: \n";
for (auto const& object : objects) {
object.Show();
}
std::cout << "Kontener ptrs: \n";
for (auto const& ptr : ptrs) {
ptr->Show();
}
}
Dodałbym jeszcze:
- Mniejsza liczba alokacji.
std::vector< Object >( n )
to 1 alokacja,std::vector< Object* >( n )
to 1+n alokacji. - W kontenerach, gdzie jest zagwarantowana ciągłość pamięci (jak np.
std::vector
,std::array
) jest dodatkowy plus przy przetwarzaniu. Algorytmy nie muszą skakać po pamięci, tylko wszystko mają "na miejscu" (cache-friendly)
tajny_agent napisał(a):
- Mniejsza liczba alokacji.
std::vector< Object >( n )
to 1 alokacja,std::vector< Object* >( n )
to 1+n alokacji.
w przypadku std::vector< Object >( n )
alokujemy sam wektor a we wskaźnikach ? Nie rozumiem :/