abstrakcyjne klasy bazowe...

0

Cześć podczas przerabiania ksiązki o c++ doszedłem do tematu dziedziczenia/polimorfizmu/funkcji wirtualnych - czysto wirtualnych
oraz abstrakcyjnych klas bazowych. Przerabiając ćwiczenia z tych rozdziałów natrafiłem na kilka problemów i w związku z tym mam do was kilka pytań...

Program: używam abstrakcyjnej klasy bazowej c_base oraz 2 klasy 'konkretne' - pochodne czyli klase c_user i c_root program rozdzielony na 3 pliki - definicja klas w pliku hede.h definicje metod klas w pliku definit.cpp i program glowny w pliku system c++.cpp
kod programu:
hede.h

#ifndef hede_h
#define hede_h
#include <iostream>
#include <cstring>
#include <cstdlib>
using std::cin;
using std::cout;
using std::endl;
using std::string;


class c_base
{
    private:
        string name;
        long int size;
        
    public:
        c_base(string n1 = "brak nazwy");
        
        virtual void set();
        virtual void out() =0;
};

class c_user : c_base
{
    private:
        string text;
        
    public:
        c_user(string t1 = "untitle");
        
        virtual void set();
        virtual void out();
};

class c_root : c_base
{
    private:
        string password;
        
    public:
        c_root(string p1 = "root");
        
        virtual void set();
        virtual void out();
};

#endif

definit.cpp

#include "hede.h"

c_base::c_base(string n1) : name(n1), size(n1.size())
{
    //-----------------
}

void c_base::set()
{
    cout<< "name: ";
    getline(cin, name);
    size = name.size();
}

void c_base::out()
{
    cout<< "name: " << name <<endl;
    cout<< "size: " <<size <<endl;
}



c_user::c_user(string t1) : c_base(), text(t1)
{
    //-----------
}

void c_user::set()
{
    c_base::set();
    cout<< "text: ";
    getline(cin, text);
}


void c_user::out()
{
    c_base::out();
    cout<< "text: " << text <<endl;
}



c_root::c_root(string p1) : c_base(), password(p1)
{
    //-----------
}

void c_root::set()
{
    c_base::set();
    cout<< "password: ";
    getline(cin, password);
}

void c_root::out()
{
    c_base::out();
    cout<< "password: " << password <<endl;
}

główny program system c++.cpp

#include "hede.h"
#include "definit.cpp"


int main()
{  
     c_user zmienna1;
     zmienna1.set();
     zmienna1.out();
     cout<<endl;
     
     c_root zmienna2;
     zmienna2.set();
     zmienna2.out();          

      
    system("PAUSE");
}

pytanie 1: zawsze dołączałem (chociaż nigdy nie rozumiałem czemu tak powinno być) plik nagłówkowy .h do definicji metod i później tez do programu głównego w tym wypadku to nie działa muszę zainkludować plik definit.cpp przypuszczam ze ma to związek z funkcjami wirtualnymi ale czy na pewno?
I czy w takim wypadku ma to jakieś znaczenie czy dołączam plik .h czy .cpp jeśli tak to jakie? zaznaczam ze w pliku .h jak widać w kodzie zabezpieczam się przed wielokrotnym podpięciem go #ifndef i tak dalej......

pytanie 2:abstrakcyjne klasy bazowe - faktyczne ułatwienie?
napisałem abstrakcyjna klasę bazowa która zawiera dane i metody których będe używał w innych obiektach ok musiałem napisać wszystkie ich definicje bo później z klas 'konkretnych' nie mam
dostępu do danych prywatnych....
każde dodanie nowych zmiennych w abstrakcyjnej klasie bazowej co ma mi ułatwić życie bo wystarczy ze dodam je tylko tam wiąże się z przerobieniem metod klasy bazowej aby móc zainicjalizować a
dalej wykonywać na nich operacje w klasach 'konkretnych'.....

A każdy pomysł na nowa metodę w której chciałbym wykorzystywać owe zmienne z abstrakcyjnej klasy bazowej sprowadza się znów do napisania takowej metody w tejże klasie po czym wywoływanie jej w klasach pochodnych c_base::jakas_nowa_metoda();

Jak czytałem ten rozdział to rozumiałem idee abstakcyjnych klas bazowych i podobało mi się to ale w momencie
kiedy chciałem to wykorzystać w bardzo prostym programie trochę mnie to.....zastanowiło ;)
być może dlatego ze nie potrafię z racji tego ze jestem początkujący jeżeli chodzi o c++ wykorzystać
pełnych możliwości tego mechanizmu ale po napisaniu tego kodu zadałem sobie pytanie 'nie prościej byłoby napisać 2 osobne nie powiązane niczym klasy? łatwiejsza modyfikacja i rozbudowa programu....
a może wyjściem jest ujęcie danych w klasie bazowej jako protected aby mieć do nich w klasach pochodnych 'łatwiejszy' dostęp?
przepraszam za tak długiego posta rozpisałem się ale chciałem abyście mnie dobrze zrozumieli
z góry dziękuje za wszystkie odpowiedzi pozdrawiam ;)

1

W pliku system c++.cpp powinieneś includować plik .h, nie .cpp. Jakie błędy Ci się pojawiają? W takim wypadku jak Ty to robisz pojawią się problemy jak będziesz musiał używać swoich klas w kilku plikach cpp.

Jeżeli chodzi o idee klas abstrakcyjnych to fajnie to wychodzi przy implementacji interfejsów. Np. coś takiego:

class sortowanie
{
public:
    virtual void sortuj(whatever) = 0;
};

class QuickSort : public sortowanie
{
public:
    void sortuj(whatever){...}
};

class BubbleSort : public sortowanie
{
public:
    void sortuj(whatever){...}
};

void allSort(whatever, sortowanie *algo)
{
    algo->sortuj(whatever);
}

int main()
{
    BubbleSort a;
    QuickSort b;

    allSort(whatever, &a);
    allSort(whatever, &b);
}

Ujęcie pól klasy bazowej protected jest poprawnym rozwiązaniem i po to to słowo kluczowe istnieje.

0
  1. To jest napisane błędnie bo twoja funkcja out nie powinna mieć żadnej definicji!
  2. Nie includuje się plików źródłowych! Pliki źródłowe sie kompiluje a potem linkuje wszystko razem. Czemu musiałeś go includować? Bo zapewne nie umiesz skompilować poprawnie programu składającego się z wielu plików źródłowych.
  3. Istnieje taki zasięg jak "protected", to jest private ale dostępny dla klas pochodnych, ale to zwykle średnie rozwiązanie ... Poza tym gadasz głupoty - nie musisz wcale w klasie pochodnej pisać nowej metody z której wywołujesz metodę klasy bazowej. Jak takiej nie napiszesz to automatycznie wywoła sie metoda klasy bazowej.
    Twój problem polega na tym że nie rozumiesz do czego to służy.
    Klasy Abstrakcyjne
0

byku_guzio jak dołącze tylko plik .h wyrzuca

[Linker Error] undefined reference to `c_user::c_user(std::string)'
[Linker Error] undefined reference to `c_user::set()'
[Linker Error] undefined reference to `c_user::out()'
[Linker Error] undefined reference to `c_root::c_root(std::string)'
[Linker Error] undefined reference to `c_root::set()'
[Linker Error] undefined reference to `c_root::out()'
[Linker Error] undefined reference to `vtable for c_base'
[Linker Error] undefined reference to `vtable for c_user'
[Linker Error] undefined reference to `vtable for c_root'
ld returned 1 exit status

Shalom zdaje sobie sprawę z mojej niewiedzy wierz mi dlatego właśnie czytam o tym a jeśli czegoś dalej nie rozumiem pisze tutaj....
wywołanie metody klasy bazowej w klasach pochodnych nie jest problemem bo tak jak w funkcji out() robię c_base::out(); i po problemie ale kiedy dodam nowa zmienna do klasy bazowej i chce na niej operować w różny sposób to muszę te wszystkie metody definiować w klasie bazowej bo w pochodnych nie mam do nich dostępu chyba ze użyje tak jak napisał to byku_guzio protected dla tych zmiennych wtedy byłoby ok ale znowu Ty mówisz ze to 'średnie rozwiązanie'.....

0

@emacs nie rozumiesz pojęcia "linkować". Tu nie chodzi o coś na poziomie kodu, tylko na poziomie kompilatora! Stwórz PROJEKT w IDE z którego korzystasz, dodaj do niego pliki i buduj cały projekt. Klikanie kompilacji tylko samego pliku z main da właśnie takie efekty...

0

ok trochę namieszałem....

class c_base
{
    protected:
        string name;
        string text;

    public:
        virtual void set() =0;
        virtual void out() =0;
};

//------------------------------------

class c_user : c_base
{
    private:
        int size;

    public:
        virtual void set();
        virtual void out();
};

void c_user::set()
{
    cout<< "name: ";
    getline(cin, name);
    cout<< "text: ";
    getline(cin, text);
    cout<< "size: ";
    cin>> size;
}

void c_user::out()
{
    cout<< "name: " << name <<endl;
    cout<< "text: " << text <<endl;
    cout<< "size: " << size <<endl;
}

w klasie bazowej zmienne używane w wielu klasach pochodnych (tu jest tylko jedna z nich korzystająca dla uproszczenia przykładu) umieszczam jako chronione aby mieć do nich bezpośredni dostęp z tychże klas oraz umieszczam prototypy wszystkich funkcji używanych przez klasy pochodne jako czysto wirtualne a następnie dla każdej klasy bazowej umieszczam własne definicje tych funkcji?
dobrze rozumiem ten mechanizm?

0

Nie. W klasie bazowej umieszczasz te metody które maja sens dla klazy bazowej. Na przykład jeśli masz klasę bazową Zwierze to nie ma sensu wrzucać do niej metody bawSięWełną() bo to metoda specyficzna dla Kota, ani gońKota() bo to metoda klasy Pies.
Ale ma sens wrzucenie tam metody jedz() albo pij() bo to metody wspólne dla zwierząt.

0

ok ale zakładam ze nie będę wogólę tworzył obiektów klasy bazowej ma to być tylko 'interfejs' który łączy cechy wspólne (zmienne i metody) innych wykorzystywanych klas....
w klasie bazowej mam zmienne które będzie wykorzystywała większa ilość klas jak name czy text i metode out() która te dane wyświetla ale jeżeli w którejś z klas pochodnych pojawi sie nowa zmienna która chce wyświetlić to muszę metodę out() definiować w niej ponownie albo użyć

void c_user::out()
{
     c_base::out();
     // i tu wyświetlam nowe zmienne klasy pochodnej......
}
0

@Shalom, on dobrze zrozumiał ;)

Tak, mniej więcej o to chodzi w polimorfizmie. Tyle, że trochę wymieszałeś implementację z interfejsem. Klasa c_base niby jest interfejsem (funkcje czysto wirtualne) ale mimo to zwiera część implementacji (pola chronione). Tak więc zaimplementuj już pewną funkcjonalność w klasie c_base:

class c_base
{
    protected:
        string name;
        string text;
  
    public:
        virtual void out()
        { 
            cout<< "name: " << name <<endl;
            cout<< "text: " << text <<endl;
        }
};

class c_user : c_base
{
    private:
        int size;
 
    public:
        virtual void out()
        { 
            c_base::out();
            cout<< "size: " << size <<endl;
        }
};

a oprócz tego możesz wydzielić interfejs:

class i_base
{
    public:
        virtual void out() =0;
};
 
class c_basic_base : i_base
{
    protected:
        string name;
        string text;
  
    public:
        virtual void out()
        { 
            cout<< "name: " << name <<endl;
            cout<< "text: " << text <<endl;
        }
};

class c_user : c_basic_base
{
    private:
        int size;
 
    public:
        virtual void out()
        { 
            c_basic_base::out();
            cout<< "size: " << size <<endl;
        }
};
0

chyba zaczynam rozumieć.... dzięki temu mogę tworzyć kontenery/tablice kilku róznych obiektów

i_base * base[2];

    base[0] = new c_basic_base;
    base[1] = new c_user;

    base[0]->out();

    base[1]->out();

......

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