• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Przeciążanie operatora ostream dla klasy szablonowej

0 głosów
611 wizyt
pytanie zadane 22 stycznia 2018 w C i C++ przez syzmon9 Początkujący (250 p.)

Jestem w trakcie tworzenia projektu w którym mam do wykonania klasę szablonową opartą na kontenerze Vector, oraz dwie klasy: bazową i dziedziczącą po niej klasę pomocniczą. Klasa A (bazowa) 3 stringi tak samo jak klasa pomocnicza B (w taki sposób że przy wywołaniu klasy B wyświetla się 6 stringów).

Klasa A:

class A 
{
protected:
	string jedenA, dwaA, trzyA;
public:
	A(string jeden, string dwa, string trzy);
	A(){}
	virtual void show(ostream& os) const
	{
		os << jedenA << " " << dwaA << " | " << trzyA << endl;
	}
};

Klasa B:

class B : public A{
	string jedenB, dwaB, trzyB;
public:
	B(string mdwa, string mjeden, string zero, string jeden, string dwa, string trzy);
	void show(ostream& os) const
	{
		os << A::jedenA <<" "<< A::dwaA << " | " << A::trzyA << " " << jedenB << " " << dwaB << " " << trzyB << endl;
	}};

Klasa Kontener jest klasą szablonową i zawiera metodę dodaj (dodaje obiekt A lub B do vectora) oraz przeciążony operator ostream:

template <typename T>
class Kontener
{
	vector<T> pojemnik;
public:
	void dodaj(const T AlubB) {pojemnik.push_back(AlubB);};
	friend ostream& operator<<(ostream& os, const Kontener<T>& k)
	{
        for (int i = 0; i < k.pojemnik.size(); ++i)
		{
               os << k.pojemnik[i]; 
        }
	return os; 
    }
};

W programie głównym utworzyłem zmienną typu Kontener<A*> żeby umożliwić dodawanie do vectora obiektów A lub B. W skrócie wygląda to w ten sposób:

int main()
{
	Kontener<A*> kont;
    kont.dodaj(new A(str1, str2, str3));
	kont.dodaj(new B(str1, str2, str3, str4, str5, str6));

    cout << kont <<endl;
    return 0;
}

Problem jest z operatorem. Mianowicie nie wyświetla on wartości ukrytych pod wskaźnikiem tylko adresy wskaźników. W jaki sposób zmienić operator ostream z klasy Kontener (bo podejrzewam że w tym miejscu siedzi problem) tak żeby wyświetlały si wartości? Czy jest możliwość dostania się do metody show z klasy A i B w tym operatorze ostream?

2 odpowiedzi

0 głosów
odpowiedź 22 stycznia 2018 przez mokrowski Mędrzec (158,960 p.)
edycja 22 stycznia 2018 przez mokrowski

Hmm... ujmę to tak... "bardzo nieciekawy tok myślenia" :-/ Ograniczyłem się jedynie do poprawienia rażących błędów i domyślania się co chciałeś wyrazić :-/

#include <iostream>
#include <string>
#include <vector>

using namespace std;

class A
{
protected:
    string jedenA, dwaA, trzyA;
public:
    A(const string& jeden, const string& dwa, const string& trzy)
        : jedenA(jeden), dwaA(dwa), trzyA(trzy) {}
    A() {}
    virtual void show(ostream& os) const
    {
        os << jedenA << " " << dwaA << " | " << trzyA << endl;
    }
    virtual ~A() {}
};

class B : public A {
    string jedenB, dwaB, trzyB;
public:
    B(const string& mdwa, const string& mjeden, const string& zero, const string& jeden, const string& dwa, const string& trzy)
        : A(jeden, dwa, trzy), jedenB(mjeden), dwaB(mdwa), trzyB(zero) {}
    virtual void show(ostream& os) const
    {
        os << A::jedenA <<" "<< A::dwaA << " | " << A::trzyA << " " << jedenB << " " << dwaB << " " << trzyB << endl;
    }
};

template <typename T>
class Kontener
{
    vector<T*> pojemnik;
public:
    void dodaj(T * AlubB) {
        pojemnik.push_back(AlubB);
    };
    friend ostream& operator<<(ostream& os, const Kontener<T>& k)
    {
        for (int i = 0; i < k.pojemnik.size(); ++i)
        {
            k.pojemnik[i]->show(os);
        }
        return os;
    }
    ~Kontener() {
        for(size_t i = 0; i < pojemnik.size(); ++i) {
            delete pojemnik[i];
        }
    }
};

int main()
{
    string str1 = "string1";
    string str2 = "string2";
    string str3 = "string3";
    string str4 = "string4";
    string str5 = "string5";
    string str6 = "string6";

    Kontener<A> kont; 
    kont.dodaj(new A(str1, str2, str3));
    kont.dodaj(new B(str1, str2, str3, str4, str5, str6));

    cout << kont << endl;
}

 

komentarz 22 stycznia 2018 przez monika90 Pasjonat (22,940 p.)
Brak destruktora wirtualnego to też jest rażący błąd.
komentarz 22 stycznia 2018 przez mokrowski Mędrzec (158,960 p.)
Racja poprawiam...
komentarz 22 stycznia 2018 przez syzmon9 Początkujący (250 p.)
Wrzuciłem tylko część kodu, żeby nie wrzucać kodu (może pozornie) niezwiązanego z problemem. Jeśli chodzi o to co potrzebuję zrobić to muszę zrobić klasę szablonową z kontenerem vector (takie wymagania i nawet jeśli można odpuścić szablon to i tak go muszę zrobić). Rozwiązałem wcześniej jeden problem podwójnego wyświetlania stringów przy wyświetlaniu ich dla klasy B. Ale teraz muszę wyświetlić zawartość kontenera i muszę uwzględnić w nim to że może w nim być obiekt klasy A jak i obiekt klasy B (rozszerzony). Kompletnie nie mam na to pomysłu a mój kod jest skutkiem wielu prób.

Co do kodu powyżej dostaję błąd że nie ma operatora który przyjmuje jako R-wartość void. Czy jest sens tworzenie kolejnego przeciążenia dla operatora?
0 głosów
odpowiedź 22 stycznia 2018 przez criss Mędrzec (172,570 p.)

Najprościej oczywiście zamienić na os << *k.pojemnik[i]; ale wtedy oczywiście nie zadziała gdy np. T=int. Więc proponuje coś takiego:

template<typename T, bool = std::is_pointer<T>::value>
struct Getter;
template<typename T>
struct Getter<T, false> { 
    static const T & get(const T & e) { return e; }
};
template<typename T>
struct Getter<T, true> { 
    static const typename std::remove_pointer<T>::type & get(const T & e) { return *e; }
};

template <typename T>
class Kontener
{
    std::vector<T> pojemnik;
public:
    void dodaj(const T AlubB) {pojemnik.push_back(AlubB);}

    friend std::ostream& operator<<(std::ostream& os, const Kontener<T>& k)
    {
        for (int i = 0; i < k.pojemnik.size(); ++i)
        {
            os << Getter<T>::get(k.pojemnik[i]);
        }
        return os;   
    }
};

C++ nie pozwala na częściowe specjalizacje szablonów funkcji i stąd ta klasa Getter niestety. To samo jesteś w stanie osiągnąć wykorzystując SFINAE albo `constexpr if` z c++17 (IMO najprostsze wyjście).

komentarz 22 stycznia 2018 przez monika90 Pasjonat (22,940 p.)
Nie pozwala na częściowe specjalizacje, ale pozwala przeciążać, co tutaj by wystarczyło.
komentarz 22 stycznia 2018 przez mokrowski Mędrzec (158,960 p.)
Brnąć można dalej... IMHO lepiej zachować dość ustabilizowaną konwencję "w szablonie goły typ a niech klasa sama wie co i jak trzyma". Osobiście nie podoba mi się bezpośrednie new/delete i nie do końca zrozumiała intencja stosowania show(...). show(...) mogło by mieć sens w kontekście pozbycia się friend.

Tu bym <type_traits> nie dotykał. Zresztą (krytykując nawet to co sam się "domyśliłem" i zostawiłem jak było), nie bardzo rozumiem linijkę 29. Dlaczego dostęp przez klasę jeśli atrybuty nie są static? No i dla 2 klas A bazowej dla  B, nie ma sensu szablon dla Kontener. Wystarczy trzymać w pojemnik wskaźnik na A.

Podobne pytania

0 głosów
1 odpowiedź 1,320 wizyt
pytanie zadane 26 listopada 2016 w C i C++ przez sofnir Gaduła (4,690 p.)
+1 głos
1 odpowiedź 311 wizyt
pytanie zadane 19 kwietnia 2018 w C i C++ przez paweto Nowicjusz (150 p.)
0 głosów
1 odpowiedź 387 wizyt
pytanie zadane 13 grudnia 2017 w C i C++ przez Zaaa Nowicjusz (160 p.)

93,729 zapytań

142,668 odpowiedzi

323,283 komentarzy

63,288 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj

Twierdza Linux. Bezpieczeństwo dla dociekliwych

Aby uzyskać rabat -10%, użyjcie kodu pasja-linux, wpisując go w specjalne pole w koszyku.

...