2014-12-03 70 views
5

Właśnie natknąłem this question która jest o tym, jak być w stanie wydrukować obiektu poprzezC++ Co jest nie tak z zastosowaniem metody toString() metoda

std::cout << x << std::endl; 

Jak zrozumiałem, standardowym sposobem osiągnięcia tego celu jest przeciążyć operatora ostreams < <. Dodaje to jednak funkcję ostream, a nie mojej klasie.

Alternatywą (również podaną jako odpowiedź na wyżej wymienione pytanie) jest zastąpienie operatora konwersji łańcuchów znaków. Jest to jednak z ostrzeżeniem o prowadzeniu do "niezamierzonych konwersji i trudnych do wykrycia błędów".

teraz zastanawiam się, czy są jakieś wady napisanie metody toString(), a następnie używając go poprzez

std::cout << x.toString() << std::endl; 
+0

Jak to zrobić dla 'int'? Czy masz na myśli 'to_string'? – doctorlove

+0

Ponieważ tworzenie czegoś, co można przesyłać strumieniowo, nie jest tym samym, co przekształcenie go w ciąg znaków. Jeśli chcesz, aby Twój typ mógł być przesyłany strumieniowo, należy przeładować 'ostream & operator <<'. Jeśli chcesz tworzyć z niego łańcuchy, daj mu to_string member. – juanchopanza

Odpowiedz

3

Strumienie wyjściowe obsługują formatowanie wyjściowe oraz wyjście. Tak ze swoimi toString() klientów metoda nie będzie w stanie zarządzać formatowanie dla obiektu tak robią wszystkiego innego:

// set specific formatting options for printing a value 
std::cout << std::scientific << std::setprecision(10) << 10.0 << '\n'; // prints 1.0000000000e+01 

// set formatting based on user's cultural conventions 
std::cout.imbue(std::locale("")); 
std::cout << 10000000 << '\n'; // depending on your system configuration may print "10,000,000" 

Być może nie obchodzi, aby umożliwić dowolne formatowanie, więc być może to nie będzie miało znaczenia, .

Innym zagadnieniem jest to, że wyjście do strumienia nie wymaga, aby cała reprezentacja ciągów znajdowała się w pamięci naraz, ale metoda toString() to robi.


inni zwrócili na to uwagę, ale myślę, że wyraźniejszy sposób, mówiąc, że jest to, że interfejs klasy nie jest ograniczone tylko do metod to świadczy, ale zawiera także inne funkcje, zbudować wokół niego, w tym niekonwencjonalnych - funkcje potomne, takie jak przeciążenia, które zapewniasz, operator<<. Nawet jeśli nie jest to metoda twojej klasy, powinieneś myśleć o niej jako o interfejsie twojej klasy.

Oto artykuł, który mówi o tym, które być może znajdziesz pomocne: How Non-Member Functions Improve Encapsulation


Oto prosty przykład przeciążenia operator<< przez użytkownika określonej klasie:

#include <iostream> 

struct MyClass { 
    int n; 
}; 

std::ostream &operator<< (std::ostream &os, MyClass const &m) { 
    for (int i = 0; i < m.n; ++i) { 
    os << i << ' '; 
    } 
    return os; 
} 

int main() { 
    MyClass c = {1000000}; 
    std::cout << c << '\n'; 
} 
+0

co z klas abstrakcyjnych? Mogę mieć toString() = 0; ale jak zadeklarować, że każda klasa potomna powinna pochodzić z przeciążonego operatora < user463035818

+0

Może powinienem najpierw przeczytać link, który opublikowałeś;) Tam jest pierwszy punkt, który mówi, że "jeśli (f musi być wirtualny) spraw, by f była funkcją członka". – user463035818

+0

@ tobi303 Tak, na razie funkcje wirtualne muszą być członkami. Możliwe, że funkcje niebędące członkami mogą uzyskać wsparcie dla wirtualnej wysyłki w przyszłości: http: // www.stroustrup.com/multimethods.pdf – bames53

1

Twoje pierwsze założenie jest błędne. Nie musisz wprowadzać żadnych zmian w ostream.

Operator, taki jak operator < <, można zdefiniować na dwa sposoby: jako metodę klasy ostream, biorąc swój obiekt x jako parametr lub jako zwykłą starą funkcję z dwoma parametrami, przyjmując ostream jako pierwszy parametr i twój obiekt x jako drugi parametr.

4

Jak zrozumiałem, standardowym sposobem osiągnięcia tego jest przeciążenie operatora ostreams < <. Dodaje to jednak funkcję ostream, a nie mojej klasie.

I to jest dobre. Im mniejsza klasa, tym lepiej. Jeśli popularny idiom w języku C++ pozwala ci mieć jeszcze jedną rzecz ze swojej klasy, dlaczego nie zastosować go?

teraz zastanawiam się, czy są jakieś wady napisanie metody toString()

Wadami są:

  • operator<< działa w sposób jednolity z wbudowanych typów (jak int) i typy zdefiniowane przez użytkownika. toString może być dostępny tylko dla klas
  • C++ jest bardziej niejednorodny niż Java. std :: string jest najpopularniejszym ciągiem, ale wciąż istnieją inne klasy ciągów i są one używane.
  • należy utworzyć ciąg znaków, który potencjalnie może pochodzić z wydajnością. Jeśli piszesz do strumienia bezpośrednio, unikasz tego.
+0

Te minusy mają znacznie mniejszą wagę niż zalety stosowania jednolitej metody serializacji z dowolną klasą. Dlaczego powinniśmy dbać o złe praktyki, takie jak niestosowanie std :: string? –

0

Nie ma nic złego w funkcji toString() na klasę. Ma tę zaletę, że jest bardziej wyraźny, może być utworzony z polimorfów i może być używany w innych sytuacjach niż streaming, ale musisz mieć regułę kodowania podczas pracy z zespołem ("było to to_string() lub str() lub streaming()? ").

Przeciążenie operatora < < jest bardziej idiomatyczne. Po prostu przyjmuje parametr ostream jako parametr i sprawia, że ​​konwersja jest niejawna podczas przesyłania strumieniowego, ale ponieważ jest to idiomatyczne w C++, większość będzie oczekiwać przeciążenia operatora < < podczas oglądania std::cout << x;.

3

Są to zasadniczo różne rzeczy. Zapewnienie przeciążenia operator<< skutecznie rozszerza interfejs strumienia, sprawiając, że obiekty klasy klasy mogą być przesyłane strumieniowo. Zapewnienie funkcji toString rozszerza interfejs twojej klasy, dzięki czemu można uzyskać std::string od klasy. Te reprezentują różne rzeczy.

Interfejs klasy powinien całkowicie odpowiadać temu, co reprezentuje w logice programu (pojedyncza odpowiedzialność). Rzadko jest to toString naturalna część interfejsu klasy. Jednak rozszerzenie interfejsu strumienia, aby zaakceptować więcej obiektów, jest znacznie bardziej logiczne.

Oznacza to, że w jednym przypadku mówisz "Teraz możesz przesyłać strumieniowo obiekty tego typu klasy". W drugim przypadku mówisz "Możesz zamienić obiekty tego typu klasy na std::string." - tak się składa, że ​​std::string można następnie przesyłać strumieniowo. Pomyśl o tym - czy naprawdę ma sens, aby moja klasa miała funkcję ? Od kiedy mogę zmienić ludzi w tekst?

+3

Zgaduję, czy to ma sens, to kwestia gustu. Moim zdaniem całkowicie ma sens, że dowolny obiekt Java można przekonwertować na ciąg przez toString() – user463035818

Powiązane problemy