Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

[C++] Program bazujący na UML - problem z dziedziczeniem i polimorfizmem

Ostatnio zmodyfikowano 2017-11-07 23:47
Autor Wiadomość
selveee
Temat założony przez niniejszego użytkownika
[C++] Program bazujący na UML - problem z dziedziczeniem i polimorfizmem
» 2017-11-07 22:19:42
Witam, ponownie ja z problemem dotyczącym rozszerzenia programu omawianego w moim poprzednim temacie:

Poprzedni temat

Program nie jest identyczny jak w powyższym przypadku, dla ułatwienia ( obowiązkowo w sumie :) ) wrzucę na nowo jak wygląda w momencie w którym działa ( bo mimo wszystko różni się w jakimś stopniu ze względu na zadania z instrukcji ):

Probka.h
C/C++
#ifndef _Probka_h_
#define _Probka_h_
/*==============*/
#include <iostream>
using namespace std;
class Probka
{
public:
    double t;
    double x;
    Probka();
    Probka( double _t, double _x )
    {
        t = _t;
        x = _x;
    }
    friend ostream & operator <<( ostream & _stream, const Probka & _probka );
};
#endif

SygnalProbkowany.h
C/C++
#ifndef _SygnalProbkowany_h_
#define _SygnalProbkowany_h_
/*==============*/
#include "Sygnal.h"
#include <iostream>
#include <vector>
using namespace std;
class SygnalProbkowany //:public Sygnal
{
private:
    vector < Probka > probki;
public:
    SygnalProbkowany() { }
    void dodajProbke( const Probka & _p )
    {
        probki.push_back( _p );
    }
    int iloscProbek() const
    {
        return probki.size() - 2;
    }
    Probka & operator []( int i )
    {
        return probki[ i ];
    }
    const Probka & operator []( int i ) const
    {
        return probki[ i ];
    }
    friend ostream & operator <<( ostream & _stream, const SygnalProbkowany & _sygnal );
};
#endif

SygnalLoader.h
C/C++
#ifndef _SygnalLoader_h_
#define _SygnalLoader_h_
/*====================*/
#include "SygnalProbkowany.h"
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
class SygnalLoader
{
public:
    SygnalLoader() { }
    SygnalProbkowany wczytajSygnal( string _nazwaPliku )
    {
        ifstream plik;
        plik.open( _nazwaPliku.c_str(), ios::in );
        string linia;
        SygnalProbkowany sp;
        double liczba1, liczba2;
        char znak;
        while( getline( plik, linia ) )
        {
            plik >> liczba1 >> znak >> liczba2;
            sp.dodajProbke( Probka( liczba1, liczba2 ) );
        }
        plik.close();
        return sp;
    }
    void zapiszSygnal( SygnalProbkowany & _sygnalp, string _nazwaPliku )
    {
        ofstream plik;
        plik.open( _nazwaPliku.c_str(), ios::out );
        for( int i = 1; i < _sygnalp.iloscProbek(); ++i )
        {
            plik << _sygnalp[ i ].t << ", " << _sygnalp[ i ].x << endl;
        }
        plik.close();
    }
};
#endif

AnalizatorSygnalu.h
C/C++
#ifndef _AnalizatorSygnalu_h_
#define _AnalizatorSygnalu_h_
/*=========================*/
#include "SygnalProbkowany.h"
#include <iostream>
using namespace std;
class AnalizatorSygnalu
{
public:
    AnalizatorSygnalu() { }
    double dlugosc( const SygnalProbkowany & _sygnalp )
    {
        double _min = _sygnalp[ 1 ].x;
        double _max = _sygnalp[ 1 ].x;
        for( int i = 1; i < _sygnalp.iloscProbek() - 2; i++ )
        {
            if( _sygnalp[ i ].x < _min )
                 _min = _sygnalp[ i ].x;
           
            if( _sygnalp[ i ].x > _max )
                 _max = _sygnalp[ i ].x;
           
        }
        return _max - _min;
    }
   
    double minimum( const SygnalProbkowany & _sygnalp )
    {
        double _min = _sygnalp[ 1 ].x;
        for( int i = 1; i < _sygnalp.iloscProbek() - 2; i++ )
        if( _sygnalp[ i ].x < _min )
             _min = _sygnalp[ i ].x;
       
        return _min;
    }
   
    double maksimum( const SygnalProbkowany & _sygnalp )
    {
        double _max = _sygnalp[ 1 ].x;
        for( int i = 1; i < _sygnalp.iloscProbek() - 2; i++ )
        if( _sygnalp[ i ].x > _max )
             _max = _sygnalp[ i ].x;
       
        return _max;
    }
   
    double srednia( const SygnalProbkowany & _sygnalp )
    {
        double m = 0.0;
        for( int i = 1; i < _sygnalp.iloscProbek() - 2; i++ )
             m += _sygnalp[ i ].x;
       
        if( _sygnalp.iloscProbek() > 0 )
             m /= _sygnalp.iloscProbek() - 2;
       
        return m;
    }
   
    double calka( const SygnalProbkowany & _sygnalp )
    {
        double calka = 0, dt = 0, dpole = 0;
        for( int i = 1; i < _sygnalp.iloscProbek() - 2; i++ )
        {
            dt = _sygnalp[ i + 1 ].t - _sygnalp[ i ].t;
            dpole =( _sygnalp[ i ].x + _sygnalp[ i + 1 ].x ) * dt / 2;
            calka = calka + dpole;
        }
        return calka;
    }
};
#endif

Sygnal.h
C/C++
#ifndef _Sygnal_h_
#define _Sygnal_h_
/*==============*/
#include "Probka.h"
#include <iostream>
#include <vector>
using namespace std;
class Sygnal
{
public:
    virtual double x( double t ) = 0;
    virtual void wypisz( ostream & stream );
    friend ostream & operator <<( ostream & _stream, Sygnal & _sygnal )
    {
        _sygnal.wypisz( _stream );
    }
};
#endif

SygnalSinusoidalny.h
C/C++
#ifndef _SygnalSinusoidalny_h_
#define _SygnalSinusoidalny_h_
/*==============*/
#include "Sygnal.h"
#include <iostream>
class   SygnalSinusoidalny  
    :   public   Sygnal  
{
public:
    SygnalSinusoidalny( double = 1.0, double w = 1.0, double psi = 0.0 )  
        : _a( a )
         ,   _w( w )
         ,   _psi( psi )  
    { }
    virtual ~SygnalSinusoidalny()   { }
      virtual double x( double t )   {
              return _A * sin( _w   *   t   +   _psi );
            }
    virtual void wypisz( ostream &   s )  
    {
                s   <<   "Sygnal sinusoidalny"   <<   endl;
                s   <<   " ­ A= "   <<   _A   <<   endl;
                s   <<   " ­ w= "   <<   _w   <<   endl;
                s   <<   " ­ psi= "   <<   _psi   <<   endl;
    }
private:
    double _A;
    double _w;
    double _psi;
};

A teraz do rzeczy - w obecnej chwili wszystko działa jak należy, jednak jestem zmuszony do utworzenia klasy abstrakcyjnej bazowej
Należy utworzyć abstrakcyjną klasę bazową (interfejs) Sygnal. W interfejsie
należy zadeklarować metody virtual double x(double t) = 0 oraz virtual void
wypisz(std::ostream& strumien) = 0. Klasa powinna też posiadać zaprzyjaźniony
operator<< wywołujący funkcję wypisz().

No cóż, sama klasa utworzona, widoczna powyżej - możliwe że poprawnie - działa. Problem pojawia się w momencie:
Klasa SygnalProbkowany powinna dziedziczyć po klasie Sygnal. W klasie
potomnej należy zaimplementować czysto wirtualne metody interfejsu Sygnal. Metoda x() powinna realizować wybraną metodę interpolacji (ZOH / FOH) sygnału.

Tutaj po ustawieniu dziedziczenia mam brak pomysłu wizji implementacji metod z klasy Sygnal, przepisując je z prefixem virtual otrzymuję błędy które, jak mi się zdaje, informują o czymś typu "brak implementacji jakichś metod" etc.

In function 'std::ostream& operator<<(std::ostream&, Sygnal&)':|
no return statement in function returning non-void [-Wreturn-type]|
In member function 'virtual double SygnalProbkowany::x(double)':|
warning: no return statement in function returning non-void [-Wreturn-type]|
In function `ZN6SygnalD2Ev':|
undefined reference to `vtable for Sygnal'|
In function `ZN6SygnalC2Ev':|
undefined reference to `vtable for Sygnal'|
In function `ZN16SygnalProbkowanyC1Ev':|
undefined reference to `vtable for SygnalProbkowany'|
In function `ZN16SygnalProbkowanyD1Ev':|
undefined reference to `vtable for SygnalProbkowany'|

Oczywiście podświetlone miejsca w których wypluwa błędy to class Sygnal oraz class SygnalProbkowany w ich własnych plikach ( podzielone na pliki .h ). Dodam, że w klasie Sygnal utworzyłem wirtualny destruktor.

Cóż powinienem począć w tym momencie, ręce mi opadają bo jeszcze przed wakacjami potrafiłbym uporać się ( tak sądzę ) z czymś takim, a teraz kompletna pustka w głowie :/.

P-166570
YooSy
» 2017-11-07 22:42:39
virtual ostream & wypisz( ostream & stream ) = 0;
C/C++
friend ostream & operator <<( ostream & _stream, Sygnal & _sygnal )
{
    _sygnal.wypisz( _stream );
    return _stream;
}
P-166571
selveee
Temat założony przez niniejszego użytkownika
» 2017-11-07 23:17:37
Faktycznie, co do pierwszego - niedopatrzyłem = 0 w instrukcji. Ciągle też zapominam o zwracaniu czegoś z funkcji ;p.

No cóż, przebrnięta droga z błędem wyżej przedstawionym, niestety tak łatwo być nie może. Teraz pojawił się długo przeze mnie wyczekiwany problem z związany z abstrakcją. Zastosowałem się do poleceń kolegi @YooSy i dotarłem do błędów które miałem uprzednio :(.



error: invalid abstract return type 'SygnalProbkowany'|
note:   because the following virtual functions are pure within 'SygnalProbkowany':|
note:     virtual void SygnalProbkowany::wypisz(std::ostream&)|
In member function 'SygnalProbkowany SygnalLoader::wczytajSygnal(std::string)':|
error: invalid abstract return type for member function 'SygnalProbkowany SygnalLoader::wczytajSygnal(std::string)'|
error: cannot declare variable 'sp' to be of abstract type 'SygnalProbkowany'|
error: cannot allocate an object of abstract type 'SygnalProbkowany'|
In function 'int main()':|
error: cannot allocate an object of abstract type 'SygnalProbkowany'|
error: cannot declare variable 'sp' to be of abstract type 'SygnalProbkowany'|

Rozkładam ręce :(, jakieś instrukcje, podpowiedzi wraz z wyjaśnieniem " na chłopski rozum " byłyby mile widziane, nie potrzebuję gotowego bo nie na tym człowiek się uczy. Rozumiem tyle, że jest problem z utworzeniem obiektu abstrakcyjnego dla klasy SygnalProbkowany w dwóch miejscach: main oraz SygnalLoader ( tak wnioskuję ), reszty nie potrafię odkodować ze swoim ubogim zasobem wiedzy.

Dodam aktualny wygląd klas:

SygnalProbkowany
C/C++
#ifndef _SygnalProbkowany_h_
#define _SygnalProbkowany_h_
/*==============*/
#include "Sygnal.h"
#include <iostream>
#include <vector>
using namespace std;
class SygnalProbkowany
    : public Sygnal
{
private:
    vector < Probka > probki;
public:
    SygnalProbkowany() { }
    virtual double x( double t )
    {
        return t;
    }
    virtual void wypisz( ostream & stream ) = 0;
    void dodajProbke( const Probka & _p )
    {
        probki.push_back( _p );
    }
    int iloscProbek() const
    {
        return probki.size() - 2;
    }
    Probka & operator []( int i )
    {
        return probki[ i ];
    }
    const Probka & operator []( int i ) const
    {
        return probki[ i ];
    }
    friend ostream & operator <<( ostream & _stream, const SygnalProbkowany & _sygnal );
};
#endif

Sygnal.h
C/C++
#ifndef _Sygnal_h_
#define _Sygnal_h_
/*==============*/
#include "Probka.h"
#include <iostream>
using namespace std;
class Sygnal
{
public:
    virtual ~Sygnal() { }
    virtual double x( double t ) = 0;
    virtual void wypisz( ostream & stream ) = 0;
    friend ostream & operator <<( ostream & _stream, Sygnal & _sygnal )
    {
        _sygnal.wypisz( _stream );
        return _stream;
    }
};
#endif

main
C/C++
#include "AnalizatorSygnalu.h"
#include "Sygnal.h"
#include "Probka.h"
#include "SygnalLoader.h"
#include <iostream>
using namespace std;
int main()
{
    string ZnazwaPliku, OnazwaPliku;
    cout << "Podaj nazwe pliku odczytu: "; cin >> OnazwaPliku;
    cout << "Podaj nazwe pliku zapisu: "; cin >> ZnazwaPliku;
    SygnalLoader sl;
    AnalizatorSygnalu as;
    SygnalProbkowany sp = sl.wczytajSygnal( OnazwaPliku );
    cout << "iloscProbek: " << sp.iloscProbek() << endl;
    cout << "maksimum: " << as.maksimum( sp ) << endl;
    cout << "minimum: " << as.minimum( sp ) << endl;
    cout << "srednia: " << as.srednia( sp ) << endl;
    cout << "dlugosc: " << as.dlugosc( sp ) << endl;
    cout << "calka: " << as.calka( sp ) << endl;
    sl.zapiszSygnal( sp, ZnazwaPliku );
    return 0;
}

Reszta niezmienna, problem pojawia się, tak jak wspomniałem wyżej w klasie SygnalLoader w miejscu deklaracji funkcji wczytajSygnal i w miejscu tworzenia obiektu dla klasy SygnalProbkowany w main.
P-166575
YooSy
» 2017-11-07 23:24:24
Kopiując do klasy SygnalProbkowany
virtual void wypisz( ostream & stream ) = 0;
 zadeklarowałeś ją abstrakcyjną. Nie można tworzyć obiektów klasy abstrakcyjnej.
SygnalProbkowany sp = sl.wczytajSygnal( OnazwaPliku );
?

W klasie SygnalProbkowany należy rozwinąć czysto abstrakcyjne metody dziedziczonej klasy Sygnal,
czyli
C/C++
virtual void wypisz( ostream & stream ) { /* tutaj kod */ }

Poza tym wygodnie jest zwracać referencję do strumienia z metody wypisz skoro
operator<< ma tylko wywołać tą metodę (rozumiem - wytyczne zadania).
C/C++
friend ostream & operator <<( ostream & _stream, Sygnal & _sygnal )
{
    return _sygnal.wypisz( _stream );
}
P-166576
selveee
Temat założony przez niniejszego użytkownika
» 2017-11-07 23:38:51
Chyba zaczynam rozumieć, idąc tropem Twoich wyjaśnień jeżeli utworzyłem klasę główną abstrakcyjną i dziedziczę po niej np. tjw. z klasy SygnalProbkowany to muszę utworzyć te same wirtualne metody w klasie dziedziczącej ale nie mogę utworzyć ich abstrakcyjnymi, tylko muszę nadać funkcjom jakieś polecenia do wykonania.

Jeżeli to pomoże mogę wrzucić treść zadania bo czasem to co chcę przekazać może się mijać szeroko z tym co odbiorca rozumie ;p.
P-166578
YooSy
» 2017-11-07 23:42:21
Lepiej będzie jeśli zaczniesz filtrować problematyczne fragmenty. Nie trzeba wklejać całego kodu,
tylko te w których jest problem, plus błędy kompilacji. Tekst zadania może być przydatny,
choć lepiej jeśli sam ją zrozumiesz.
P-166580
selveee
Temat założony przez niniejszego użytkownika
» 2017-11-07 23:47:36
Mimo wszystko wolę dawać więcej niż mniej - dam więcej to nic się nie stanie, dam mniej to może być wybrakowane :).

Co do treści zadania: Treść zadania

Pokombinuję już po pobudce bo za dużo rzeczy na raz ^_^. Dziękuję za pomoc i sugestie, jak tylko ponownie się za to chwycę to na pewno dam o sobie znać.
P-166581
« 1 »
  Strona 1 z 1