Wektor referencji czy referencja wektora?

0

Mam "problem" z zaimplementowaniem w C++ tego, co Java używa od razu. Mianowicie chodzi mi o referencję. Jak wiadomo, w Javie wszystko jest referencją (tak upraszczając;))

Ja mam pewną klasę - User. w programie głównym chcę utworzyć wektor zawierający referencje do obiektów klasy User. Nie ma w tym nic dziwnego - to samo bym osiągnął pisząc to w Javie, z użyciem listy i swojej klasy. (Czy na pewno?) Czy wektor referencji: vector<User&> w; to to samo co lita w Javie z obiektami klasy User?

A jakbym użył czegoś takiego: vector<User> &w; to byłoby to dobre rozwiązanie? Czy niekoniecznie? Podkreślam, że zależy mi na osiągnięciu funkcjonalności takiej, jaką ma Java. (A w Javie nie mogę tego napisać; MUSZĘ w Cpp;))

Proszę o pomoc.Deklaracja czegoś takiego: vector<User&> w; daje mi takie błędy:

/usr/include/c++/4.6/bits/allocator.h:92|11|instantiated from ‘std::allocator<User&>’|
/usr/include/c++/4.6/bits/stl_vector.h:73|60|instantiated from ‘std::_Vector_base<User&, std::allocator<User&> >’|
/usr/include/c++/4.6/bits/stl_vector.h:180|11|instantiated from ‘std::vector<User&>’|
/home/ja/tester.cpp:17|19|instantiated from here|
/usr/include/c++/4.6/ext/new_allocator.h|59|error: forming pointer to reference type ‘User&’|
/usr/include/c++/4.6/ext/new_allocator.h|60|error: forming pointer to reference type ‘User&’|
/usr/include/c++/4.6/bits/stl_vector.h:73|60|instantiated from ‘std::_Vector_base<User&, std::allocator<User&> >’|
/usr/include/c++/4.6/bits/stl_vector.h:180|11|instantiated from ‘std::vector<User&>’|
/home/ja/tester.cpp:17|19|instantiated from here|
/usr/include/c++/4.6/bits/allocator.h|97|error: forming pointer to reference type ‘User&’|
/usr/include/c++/4.6/bits/allocator.h|98|error: forming pointer to reference type ‘User&’|
/home/ja/tester.cpp:17|19|instantiated from here|
/usr/include/c++/4.6/bits/stl_vector.h|206|error: no members matching ‘std::vector<User&>::_Base {aka std::_Vector_base<User&, std::allocator<User&> >}::_M_allocate’ in ‘std::vector<User&>::_Base {aka struct std::_Vector_base<User&, std::allocator<User&> >}’|
/usr/include/c++/4.6/bits/stl_vector.h|207|error: no members matching ‘std::vector<User&>::_Base {aka std::_Vector_base<User&, std::allocator<User&> >}::_M_deallocate’ in ‘std::vector<User&>::_Base {aka struct std::_Vector_base<User&, std::allocator<User&> >}’|
/home/ja/tester.cpp||In function ‘int main()’:|
/home/ja/tester.cpp|18|error: ‘uzytk’ was not declared in this scope|
/home/ja/tester.cpp|21|warning: comparison between signed and unsigned integer expressions [-Wsign-compare]|
/usr/include/c++/4.6/bits/stl_vector.h||In destructor ‘std::vector<_Tp, _Alloc>::vector() [with _Tp = User&, _Alloc = std::allocator<User&>]’:|
/home/ja/tester.cpp:17|19|instantiated from here|
/usr/include/c++/4.6/bits/stl_vector.h|350|error: ‘struct std::_Vector_base<User&, std::allocator<User&> >::_Vector_impl’ has no member named ‘_M_start’|
/usr/include/c++/4.6/bits/stl_vector.h|350|error: ‘struct std::_Vector_base<User&, std::allocator<User&> >::_Vector_impl’ has no member named ‘_M_finish’|
/usr/include/c++/4.6/bits/stl_vector.h||In member function ‘std::vector<_Tp, _Alloc>::size_type std::vector<_Tp, _Alloc>::size() const [with _Tp = User&, _Alloc = std::allocator<User&>, std::vector<_Tp, _Alloc>::size_type = unsigned int]’:|
/home/ja/tester.cpp:21|32|instantiated from here|
/usr/include/c++/4.6/bits/stl_vector.h|571|error: ‘const struct std::_Vector_base<User&, std::allocator<User&> >::_Vector_impl’ has no member named ‘_M_start’|
/usr/include/c++/4.6/bits/stl_vector.h|571|error: ‘const struct std::_Vector_base<User&, std::allocator<User&> >::_Vector_impl’ has no member named ‘_M_finish’|
/usr/include/c++/4.6/bits/stl_vector.h||In destructor ‘std::_Vector_base<_Tp, _Alloc>::
_Vector_base() [with _Tp = User&, _Alloc = std::allocator<User&>]’:|
/usr/include/c++/4.6/bits/stl_vector.h:218|15|instantiated from ‘std::vector<_Tp, _Alloc>::vector() [with _Tp = User&, _Alloc = std::allocator<User&>]’|
/home/ja/tester.cpp:17|19|instantiated from here|
/usr/include/c++/4.6/bits/stl_vector.h|142|error: ‘struct std::_Vector_base<User&, std::allocator<User&> >::_Vector_impl’ has no member named ‘_M_start’|
/usr/include/c++/4.6/bits/stl_vector.h|142|error: ‘struct std::_Vector_base<User&, std::allocator<User&> >::_Vector_impl’ has no member named ‘_M_start’|
/usr/include/c++/4.6/bits/stl_vector.h|142|error: ‘struct std::_Vector_base<User&, std::allocator<User&> >::_Vector_impl’ has no member named ‘_M_end_of_storage’|
/usr/include/c++/4.6/bits/stl_vector.h|142|error: ‘_M_deallocate’ was not declared in this scope|
/usr/include/c++/4.6/bits/stl_vector.h||In constructor ‘std::_Vector_base<_Tp, _Alloc>::_Vector_impl() [with _Tp = User&, _Alloc = std::allocator<User&>]’:|
/usr/include/c++/4.6/bits/stl_vector.h:107|17|instantiated from ‘std::_Vector_base<_Tp, _Alloc>::_Vector_base() [with _Tp = User&, _Alloc = std::allocator<User&>]’|
/usr/include/c++/4.6/bits/stl_vector.h:218|15|instantiated from ‘std::vector<_Tp, _Alloc>::vector() [with _Tp = User&, _Alloc = std::allocator<User&>]’|
/home/ja/tester.cpp:17|19|instantiated from here|
/usr/include/c++/4.6/bits/stl_vector.h|83|error: using invalid field ‘std::_Vector_base<_Tp, _Alloc>::_M_start’|
/usr/include/c++/4.6/bits/stl_vector.h|83|error: using invalid field ‘std::_Vector_base<_Tp, _Alloc>::_M_finish’|
/usr/include/c++/4.6/bits/stl_vector.h|83|error: using invalid field ‘std::_Vector_base<_Tp, _Alloc>::_M_end_of_storage’|
||=== Build finished: 18 errors, 1 warnings ===|

1

Użyj wektora wskaźników.
Wskaźnik w C++ to najbliższy odpowiednik referencji w Java (zasadniczą różnicą jest brak GC).
Referencja w C++ to trochę inny twór, specjalny rodzaj wskaźnika. Po więcej informacji odsyłam do literatury/internetu (są to podstawy języka).

0

może vector<User*> w;. dodając element do wektora pamiętaj aby przekazać adres a nie wartość. przykład: w.insert(new User(...))

0

To, co w tej chwili robisz to trochę tak jak mówienie po angielsku układając w myślach zdania po polsku i tłumacząc po jednym słowie - to nie będzie dobrze działać. Musisz odejść mentalnie od Javy, jeżeli chcesz pisać w C++. Nie ma tutaj żadnej zarządzanej sterty, nie ma GC, a referencje to bardziej sposób przekazania/zwrócenia argumentu niż uchwyt do faktycznego obiektu na stercie zarządzanej (i tak bym nazwał właśnie javove referencje, o których mówisz). Model pamięci jest inny. Po prostu.

Najbliżej zarządzanego uchwytu / referencji znanej z Javy i .NET będzie std::shared_ptr, który będzie liczył odwołania zwalniając obiekt na stercie, który nie jest już używany. Ale nie jest to dokładnie to samo, ma swoje wady i najczęściej wcale nie trzeba z niego korzystać (i lepiej tego nie robić jak absolutnie nie potrzebujesz takiej funkcjonalności).
W C++ (dla mnie) najlepszą praktyką jest bardzo jasne określenie kto jest odpowiedzialny za zwolnienie obiektu - inaczej mówiąc, najlepiej jest, gdy wiemy kto który obiekt posiada. Jeżeli zniszczymy rodzica to automatycznie zniszczą się jego dzieci. I std::vector działa właśnie w ten sposób - on posiada elementy, które w sobie trzyma. I gdy vector przestanie istnieć, przestaną też istnieć elementy, które trzyma.
Dlatego gdy tylko mogę, korzystam z std::unique_ptr, bo o ile wcześniej odpowiedzialność za obiekt to była sprawa umowna (umieszczona w dokumentacji) to teraz można wymusić ją składnią języka.

Referencji użyć nie możesz, bo referencja jako taka nie jest obiektem, nikt referencji posiadać nie może - nie da się utworzyć wskaźnika do referencji i nie da się pobrać jej adresu.

0

Ok, dziękuję za odpowiedzi. Czyli najsensowniejszym rozwiązaniem będzie użycie unique_ptr. (Ew. zamiennie = zwykłych wskaźników, a już myślałem, że tego uniknę;))

Ok, powiedzcie proszę jeszcze tylko, czy dobrą inf znalazłem, że jak używam unique_ptr to już nie muszę zaprzątać sobie głowy zwalnianiem pamięci? Czy muszę jakoś usuwać przynajmniej wskaźniki?

0

Ok, powiedzcie proszę jeszcze tylko, czy dobrą inf znalazłem, że jak używam unique_ptr to już nie muszę zaprzątać sobie głowy zwalnianiem pamięci?

Może na początek zrób vector po prostu obiektów (nie wskaźników, referencji...) i zobacz jak to działa..

Poza tym, pierwszy link z gógla po zapytaniu unique_ptr:

http://msdn.microsoft.com/en-us/library/ee410601.aspx napisał(a)

unique_ptr Class
Stores a pointer to an owned object. The object is owned by no other unique_ptr. The object is destroyed when the unique_ptr is destroyed.

0

Też przeglądałem google:)) Znalazłem coś takiego, ale nie widzę tam, aby było użyte delete, dlatego zapytałem: http://en.cppreference.com/w/cpp/memory/unique_ptr

niby pisze, że jest destruktor, a jednak...

0

Niekoniecznie zastosowanie std::unique_ptr będzie najsensowniejszym rozwiązaniem. Jeżeli nie musisz korzystać z poliformizmu to możesz w wektorze trzymać zwyczajne, "gołe" typy (std::vector<User>).

I tak, jak już Azarien przytoczył, zniszczenie wektora zniszczy poszczególne std::unique_ptr, które zniszczą obiekty, które trzymają (domyślnie wywoła delete/delete[]).

0

Ogólnie może przedstawię problem: mam pewien program, gdzie mam pewne zależności. Np. Mam klasę User i Group. W klasie User jest wektor obiektow Group (czyli grupy, do ktorych jest zapisany użytkownik) oraz w klasie Group jest obiekt użytkownik - założyciel grupy, oraz vector User, użytkowników, którzy są w danej grupie. Takich powiązań w moim programie jest więcej.

Co w takim wypadku będzie mądrym rozwiązaniem?

0

Zależy od tego jak silne te powiązania mają być. W tym przypadku, według mnie, ani użytkownik nie posiada grup (bo może istnieć bez niego), ani grupa nie posiada użytkownika (bo usunięcie z grupy nie oznacza usunięcia użytkownika). I użytkowników i grupy powinna posiadać jakaś inna klasa, a tamte powinny użyć zwykłych wskaźników - oznacza to, że nie zarządzają one życiem tych obiektów.

class System
{
public:
	std::vector<std::unique_ptr<User>> Users;
	std::vector<std::unique_ptr<Group>> Groups;
};

class User
{
public:
	std::vector<Group*> Groups;
};

class Group
{
public:
	User* Owner;
	std::vector<User*> Members;
};
0

OK, mówisz, że nie wpływa, czyli usunięcie wskaźnika nie ma wpływu na element? W takim razie dlaczego ten kod wyświetla mi najpierw

#include <iostream>
using std::cout;

int main(){


    vector<int*> w;
    int *a = new int[3];
    a[0] = 2;
    a[1] = 3;
    a[2] = 4;

    w.push_back(a);
    int j =0;
    for(int i=0; i<3; i++){
         cout << w[0][i] << "\n";
    }

    delete a;

    cout << "_____________\n";

    for(int i=0; i<3; i++){
        cout << w[0][i] << "\n";
    }

    return 0;
}

2
3
4

0
3
4

?

Jakoś nie mogę tego pojąć.

0

Wektor zapamiętał wskaźnik do "a".
Po zwolnieniu system zaznaczył że blok "a" jest zwolniony.
Wynik wyświetlenia po zwolnieniu obszaru jest nieprzewidywalny:

  • Zmieni się pierwsza wartość (jeżeli program nie zdążył użyć tej pamięci na co innego)
  • Wszystkie wartości są inne (jeżeli program zdążył użyć tej pamięci na co innego)
  • "Abnormal program termination" (jeżeli program zdążył "zwrócić" tą pamięć systemowi)
0

W sumie rozumiem - ale jednak kod "nie działa" bo wg tego, co piszesz, powinno mi wypisać dokładnie to samo. Raczej nie jest to wina kompilatora/ide używam CodeBlocks na Kubuntu 12.04.

W sumie jeśli ktoś mógłby mi doradzić, jak mądrze zaprojektować coś takiego używając wskaźników, to byłbym niesamowicie wdzięczny. Chodzi mi tylko o powiedzenie, w której klasie mam zrobić wektor wskaźników, tak jak wytłumaczył to wcześniej kolega @Rev:)

to mój programik (pseudouml) :

user image

0

@_13th_Dragon: dzięĸi, chyba juz wiem.

Patrząc ja mój schemat, ja zrobiłbym to tak:

  1. Grupa-uzytk - jak pisał @Rev
  2. Uzytk-temat - uzytk ma WEKTOR OBIEKTÓW, bo jak usunę użytkownika, to nie usuwam jego tematów, temat ma 1 OBIEKT typu użytkownik
  3. Uzytk-post - jak wyżej
  4. Temat-post - jeden temat może mieć wiele postów, jak usuwam temat, usuwam i posty -> temat ma WEKTOR WSKAŹNIKÓW do obiektów typu post, post ma OBIEKTY typu temat i uzytk
  5. forum-temat -> usuwam forum/podforum -> usuwam wszystkie tematy, forum ma WEKTOR WSKAŹNIKÓW do obiektów temat, temat może być na forum, czyli temt ma OBIEKT typu forum
  6. forum-podforum(forum) -> na forum moe być kilka podforów (potworów:D) więc jedno główne forum ma WEKTOR WSKAŹNIKÓW do podforów, a podforum każde ma OBIEKT typu forum
  7. forum-grupa - forum może mieć kilka grup, ale jak usuwam forum, to usuwam i grupę = forum ma WEKTOR WSKAŹNIKÓW do grup. grupa ma OBIEKT forum (usuwam forum, nie usuwam grupy).

To taka moja analiza wskaźników w C++ (jednak w Javie łatwiej) = jakby ktoś miał ochotę to sprawdzić, to będę bardzo wdzięczy wiedząc czy dobrze/źle myślę:))

0

Bardzo bym prosił, żeby ktoś rzucił okiem = bo nie wiem, czy na pewno dobrze to zrozumiałem:)

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