Iterator (vector) czy zwykły int i?

0

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!

1

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ć.

1

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().

0
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 zamiast int, bo taki typ zwraca metoda size().

Dokładnie tak, int podałem jako czysty przykład zmiennej.

Dzięki za odpowiedzi! :)

1
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.

3

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).

2
YooSy napisał(a):

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().

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)

1

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.

0
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.

2

Jeśli nie używasz polimorfizmu to nie widzę sensu w tworzeniu kontenera wskaźników zamiast kontenera obiektów.

0
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 ?

3

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();
   }
}
3

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)
0
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 :/

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