Jak najlepiej modyfikować składowe klasy

Jak najlepiej modyfikować składowe klasy
Silv
Moderator Wiki
  • Rejestracja:ponad 10 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Warszawa
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:

Kopiuj
struct Vertex {

private:
	int vertexNumber;
	int someProperty;

public:
	void setSomeProperty(int value);

}

oraz

Kopiuj
#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:
Kopiuj
// 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:
Kopiuj
// 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ź)


edytowany 3x, ostatnio: Silv
Pebal
Pierwszą metodę stosuje się przypadku, gdy obiekt klasy Graph musi na bieżąco reagować na zmiany zachodzące w przechowywanych obiektach.
LukeJL
  • Rejestracja:około 11 lat
  • Ostatnio:około godziny
  • Postów:8398
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".


Silv
Co masz na myśli, mówiąc "nie na obiektach w sensie <<inteligentnych bytów>>"?
LukeJL
Że nawet jak używasz słowa kluczowego class i piszesz obiektowo, to i tak nie traktujesz obiektów jako zamkniętych całości, które "coś robią" a raczej traktujesz obiekty jako (prawie) otwarte pojemniki na dane. Więc o ile nikt ci nie zabroni używać zasad z obiektówki (enkapsulacja danych) to w rezultacie jest to przelewanie wody do dziurawego naczynia, skoro potem jedynymi metodami obiektów są gettery i settery czyli metody, które z definicji pozwalają na obejście enkapsulacji danych.
Silv
@LukeJL, masz rację, można by się nad tym zastanowić na przyszłość.
_13th_Dragon
  • Rejestracja:ponad 19 lat
  • Ostatnio:2 miesiące
2
Kopiuj
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);

Wykonuję programy na zamówienie, pisać na Priv.
Asm/C/C++/Pascal/Delphi/Java/C#/PHP/JS oraz inne języki.
edytowany 2x, ostatnio: _13th_Dragon
Silv
Hm... to jest dobre, trzecie możliwe rozwiązanie. A czy jest zawsze (kiedy?) lepsze od pozostałych dwóch?
_13th_Dragon
To jest wersja 2, z uwzględnieniem tego co napisał @Endrju niżej, zrobiona na operatorach, uwzględniająca możliwość stałego Graph'a.
Silv
Masz rację. Niemniej, mnie chodziło o coś troszeczkę innego. Patrz mój post poniżej.
Endrju
  • Rejestracja:około 22 lata
  • Ostatnio:ponad rok
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 = ...).


"(...) otherwise, the behavior is undefined".
edytowany 1x, ostatnio: Endrju
Silv
Dzięki za referencję, jeszcze muszę się z nią podszkolić. :) Ale bez żadnej kontroli to chyba jednak bardzo niedobra praktyka, nie dla mnie. ;)
Silv
Moderator Wiki
  • Rejestracja:ponad 10 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Warszawa
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?


edytowany 2x, ostatnio: Silv
_13th_Dragon
  • Rejestracja:ponad 19 lat
  • Ostatnio:2 miesiące
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.


Wykonuję programy na zamówienie, pisać na Priv.
Asm/C/C++/Pascal/Delphi/Java/C#/PHP/JS oraz inne języki.
Silv
Ok... :D Tego się nie spodziewałem, aczkolwiek przecież to jedna z najprostszych możliwości. ;)
pingwindyktator
  • Rejestracja:ponad 12 lat
  • Ostatnio:17 dni
  • Lokalizacja:Kraków
  • Postów:1055
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.


do not code, write prose
Silv
To, co @Endrju powiedział, tylko z innej strony. Dzięki.
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)