Jak najlepiej modyfikować składowe klasy

0

Mam taką małą zagwozdkę; trochę teoretyczny problem, ale niech tam, opiszę na forum.

Załóżmy, że mamy przykładowe dwie klasy Vertex i Graph:

struct Vertex {

private:
	int vertexNumber;
	int someProperty;

public:
	void setSomeProperty(int value);

}

oraz

#include <vector>
#include "Vertex.hpp"

class Graph {

private:
	std::vector<Vertex> v;

public:
	const vector<Vertex> & getV() const;

}

(Załóżmy jeszcze, że v jest na tyle duże, że nie opłaca się go pobierać przez wartość [mam nadzieję, że referencja jest tutaj sensowna].)

Jeśli chciałbym zmodyfikować jakiś Vertex, czyli v[i], to wywołałbym metodę setSomeProperty(). No ale do v[i] mam dostęp tylko za pomocą getV(), która zwraca stałą referencję, więc nic z tego.

Z uwagi na powyższe, mam dwie możliwości do wyboru (dwie znam ja, może jest więcej?):

  1. Pierwsza - zdefiniować metodę setVertexSomeProperty() w klasie Graph:
// definicja
void Graph::setVertexSomeProperty(int vertexNumber, int value) {
	this->v.at(vertexNumber).setSomeProperty(value);
}

// wywołanie
Graph g();
g.setVertexSomeProperty(1, 2);
  1. i druga - zdefiniować metodę getVertex() w klasie Graph i na wyniku tej metody wywołać odpowiednią metodę z klasy Vertex:
// definicja
Vertex& Graph::getVertex(int vertexNumber) {
	return this->v.at(vertexNumber);
}

// wywołanie
Graph g();
Vertex v = g.getVertex(1);
v.setSomeProperty(2);

I teraz moje pytanie: czy któraś z tych możliwości jest lepsza, a jeśli tak, to pod jakimi względami?

PS. Rozumiem oczywiście, że jeśli referencja w metodzie getVertex jest niewłaściwa, to problem sam się rozwiązuje, bo zostaje jedna możliwość (o ile nie ma ich więcej) (@_13th_Dragon podał trzecie rozwiązanie).

PS2. Czy zamiast Vertex v = ... nie powinno być Vertex& v = ... ? (już podano odpowiedź)

0

Ja bym się zastanowił, czy w ogóle obiektówka jest potrzebna, skoro i tak operujesz na strukturach danych przede wszystkim, a nie na obiektach w sensie "inteligentnych bytów".

2
class Graph
  {
   private:
   std::vector<Vertex> v;
   public:
   size_t size()const { return v.size(); }
   Vertex &operator[](size_t idx) { return v[idx]; }
   const Vertex &operator[](size_t idx)const { return v[idx]; }
  };
...
Graph g();
g[0].setSomeProperty(2);
5

Jeżeli chcesz modyfikować składnik v (beznadziejna nazwa) bez żadnej kontroli ze strony klasy Graph to zwracaj go przez referencję. Po prostu. Proponowane rozwiązania (Twoje i to wyżej) mogą dać większą kontrolę klasie Graph nad tym co się dzieje.

Ale żeby 2. miało sens w wywołaniu musi być przypisanie do referencji (Vertex &v = ...).

0

Dzięki za wszystkie odpowiedzi, przydadzą się w nauce, jednak chyba troszeczkę źle się wyraziłem.

Chodzi mi dokładnie o to, czy któraś z opisanych metod (jednak przyjmę, że są trzy, a nie dwie, jak mówi @_13th_Dragon ;) ) jest lepsza od pozostałych i w jakich sytuacjach? Nie mam na myśli wymagań formalnych, tzn. coś nie będzie const i się nie da (choć dzięki za ewentualne wskazania takich sytuacji!), tylko bardziej, hm... stylistyczne?

0
Michał Bodziony napisał(a):

... czy któraś z opisanych metod ... jest lepsza od pozostałych i w jakich sytuacjach?
A o tym są pisane grube książki o wzorcach projektowych. Przy czym autorzy w wielu przypadkach nie są ze sobą zgodni.

2

Już zostało powiedziane chyba. Jeśli Graph nie musi mieć kontroli nad Vertex, tj jakiekolwiek działania na Vertex nie sprawią, że obiekt klasy Graph będzie w stanie niespójnym, złym, nieprawidłowym, to zwracaj to przez referencje. I najlepiej zaklep dwie formy tej funkcji:
http://ideone.com/6BNKgy
to jest bardzo częsta praktyka, sam wymyśl po co się tak robi ;)
Jeśli natomiast Graph musi mieć kontrole nad tym co dzieje się z Vertex - zrób jakiegoś dziwnego gettera / settera.

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