Newsgroup: it.comp.lang.c++
Titolo: Problema di derivazione

Ho uno scenario di questo genere:
class A : public DatiA
{
    ....... costruttore, distruttore, accesso ai dati ecc.
};
class DatiA
{
    ..... costuttori,distruttori, salvataggio dei dati in file, lettura dei
dati da file
}
class DatiB
{
    un contenitore contenete oggetti tipo A per esempio una list
    Costruttore distruttore ecc.
}
class B : public DatiB
{
    costruttori distruttori ecc.
}
class DatiC
{
    un contenitore contenete oggetti tipo B per esempio una list
    Costruttore distruttore ecc.
}
class C : public DatiC
{
    distruttore costruttore ecc.
}
tutto quanto e' contenuto in una libreria che vorrei mantenere standard.
a questo punto derivo dalle classi un oggetto che viene derivato dalla
classe C
per es.
class Esempio : public C
{
    in questo oggetto inserisco delle informazioni particolari di questo
oggetto, il metodo di salvataggio dei dati non compresi nella classe C ecc.
}
Il problema che ora mi sorge e' quello di aggiungere delle informazioni che
debbono essere appartenenti all'oggetto relativo alla classe A, ma non
vorrei modificarla perche facente parte di questa libreria. Come posso fare
a rendere modificabile la struttura dati di A senza riderivare tutte le
calssi?
Oggi io aggiungo alla classe Esempio un' altra classe che mi consente di
parallelare i dati della classe A in questo modo
class Esempio : public C, public ClasseParallela
{
.................. ecc
}
class ClasseParallela
{
    introduco le informazioni per ogni elemento di tipo A contenuto nella
classe C con un altra lista
}
tutto funziona bene ma a volte si sbaglia e si fa fatica a trovare l'errore.
Quale altra soluzione si potrebbe usare per inserire senza modificare la
classe A de nuovi dati?
L'sempio concreto si puo' trovare presso il sito
https://sourceforge.net/projects/g2dlib/
 quella e' la libreria che utilizzo
dalla quale derivo altri oggetti.
Grazie e scusate per la lungaggine
Angelo

 calssi?
Cambia il nome di A in, ad esempio, Aold e introduci una nuova classe A
derivata da Aold, con tutte le modifiche del caso. Così l'interfaccia verso le
nuove classi rimane la stessa :)
Ciao
Fisher

Si molte grazie provero' anche questa soluzione che mi sembra per le mie
applicazioni migliore di quella che sto utilizzando.
Pero' quello che sto cercando e' se possibile trovare un sistema da fornire
a chi utilizza la libreria dell'indirizzo di fare come un plug-in con i
nuovi dati senza dover ricompilare la libreria ogni volta. Scusa le idee
poco chiare ma ancora non ho ben partorito l'idea che mi sta balenando in
mente. Come se potessi passare alla classe A qualcosa di non ben definito,
ma che vado a definire al momento che utilizzo la classe, non so' come se
passassi un puntatore ad una classe della quale non conosco a priori la sua
definizione e che tra le altre cose potrebbe anche esserre NULL perche' non
mi serve. come se potessi fare  una dichiarazione del tipo:
class A : public Undefined
{
............. ecc.
}
e la classe undefined la realizzo e la definisco se la necessito nel modo e
con i metodi che voglio.
Veramente non so se sono stato chiaro, ribadisco di avere in questo momento
le idee molto confuse.
Comunque grazie Angelo.


 calssi?
Non ho capito se vuoi cambiare l'interfaccia o l'implementazione di A. Ca come
hai descritto la cosa la libreria pubblica entrambe.
Io ti consiglio di ridisegnarla e di pubblicare solo l'interfaccia, non
l'implementazione. In questo modo puoi rifare quest'ultima a piacere.
Se invece vuoi, in un caso particolare, aumentare l'interfaccia di A, non devi
fare altro che derivarla e fornire una nuova implementazione. Oppure usare il
pattern adapter(*) se la nuova interfaccia e' fissa o decorator(*) se cambia a
runtime.
(*) Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides, "Design
Patterns: Elements of Reusable Object-Oriented Software", Addison-Wesley 1995
Andrea Ferro
---------
Brainbench C++ Master. Scored higher than 97% of previous takers
Scores: Overall 4.46, Conceptual 5.0, Problem-Solving 5.0
More info 
http://www.brainbench.com/transcript.jsp?pid=2522556

con i metodi che voglio.
premetto che non ho capito benissimo il problema; cmq se la classe
Undefined č nota al tempo di compilazione puoi usare un template:
template<class Undefined>
class A: public Undefined
{
/* ... */
};
e poi istanziare un oggetto in questo modo:
class DatiA { /* ... */ };
A<DatiA> mio_oggetto;
ciao Anto
--
Antonio Cuni, 18 anni, studente
Brainbench Certification: 
Master C++: 4.44, higher than 97% of all previous test takers.
Visual Basic 5: 3.06, higher than 77% of all previous test takers.
http://www.brainbench.com/transcript.jsp?pid=1301688


Si grazie per il suggerimento anche questa potrebbe essere una buona idea.
Ti spiego meglio il problema cosi' nel caso avessi un suggerimento me lo
passi.
Attualmente ho una classe che si chiama DatiA nella quale sono immagazzinati
tutti i dati che richiedono di essere salvati riguardanti la classe A, sono
anche implementati i metodi per l'accesso ai dati e per scrivere e leggere
questi dati in un file. Poi troviamo la classe A nella quale e' contenuto
l'oggetto A con i suoi dati locali (che non debbono essere salvati) e i
metodi dell'oggetto.
Da questo punto io derivo un oggetto che e' una classe che contiene nel tipo
dati DatiB una collezione dell'oggetto A (dati da salvare e rileggere) e
nella calasse B l'oggetto con metodi e dati locali.
Poi ho una classe DatiC che contiene una collezione di oggetti tipo B  e una
classe C che contiene i metodi e i dati locali all'oggetto C.
Per descrivere ancora meglio diro' che nella classe A immagazzino le
coordinate dei punti, nella classe B colleziono i punti formanti una linea e
nella classe C colleziono le linee che formano una curva interrotta da
spigoli. Il problema che ho e' che alcuni utilizzatori della libreria che ho
scritto potrebbero avere la necessita' per esempio di definire dei layer a
livello di classe A senza per questo dover ricodificare la classe A con
possibili errori, quindi estendendola dal basso e rendendo accessibili da C
i singoli valori immagazzinati in A.
Quindi stando al tuo suggerimento potrei definire un template in questo
modo:
class DatiA
{
    /*-----*/
};
template<class UndefinedA>
class A: public DatiA, public UndefinedA
{
    /*....*/
};
class DatiB
{
    conteiner<A>    Contenitore_di_dati_tipo_A;
    /*-------*/
};
class B : public DatiB
{
    /*-------*/
};
class DatiC
{
    container<B>  Contenitore_di_dati_tipo_B;
    /*------*/
};
class C : public DatiC
{
    /*-------*/
};
//-------- Fino a qui la libreria
//---------ora implemento l'oggetto di cui ho bisogno
class    Definisci_estensione_di_A{
    int colore;
    int layer;
    /*------*/
};
class Oggetto : public C // qui sorge il mio problema come posso fare?
{
    /*-------*/
};
//---------E ora creo un magazzino dove mettere gli Oggetti
    container<Oggetto>    Magazzino_oggetti;    //pronto per essere usato
La logica che vedi senza l'estensibilità gia' funziona nella libreria e mi
consente di espandere quest'ultima creando nuovi oggetti con le
caratteristiche richieste, posso anche definire nell'oggetto con dati che
non sono compresi nella libreria, ma dipendenti dalle caratteristiche del
prodotto che sto creano. Il problema e' se questi dati li ho bisogno a
livello dell'oggetto A senza modificare A e ereditando le nuove
caratteristiche di A attraverso la propagazione ereditaria.
Spero di aver descritto meglio il problema.
Grazie Angelo

 A<DatiA> mio_oggetto;
Si possono usare i template anche per la derivazione ?
Questa non la sapevo davvero... se ne scopre sempre una nuova col C++ :-)
Massimo

 i singoli valori immagazzinati in A.
Non mi e' chiaro cosa intendi con "definire dei layer a livello di classe A".
Per descrivere ancora meglio forse potresti postare le tue classi (quelle
attuali, non inventate per l'occasione). Ometti tutti i body dei metodi, lascia
solo le dichiarazioni.
E anche un esempio di utilizzatore che definisce un layer a livello di A (o
almeno di cosa vorrebbe fare e perche').


 Andrea Ferro
Dunque le classi le trovi a questo indirizzo e le puoi scaricare
tranquillamente sono GPL.
https://sourceforge.net/projects/g2dlib/
e la trovi anche un programma funzionante con le primitive derivate dalle
classi base.
Per quanto riduarta i Layer immagina una maschera di bit ogni bit
corrisponde ad un Layer se il bit e' acceso la classe A appartiene a questo
Layer se e' spento no.
La classe per la gestione di questo te la posto qui'.
//--------------------------------------------------------------------------
-
//TLaier.h
#ifndef TLaierH
#define TLaierH
//--------------------------------------------------------------------------
-
class TLaier
{
   private:
      AnsiString  FDato;    //ansi string == string of char
      mutable  unsigned int fhandler;  //the handler of the element (memory
location where the element
                                       //is stored in this way not possible
to have the same value
                                       //for two variable
      void SetBit(int number,bool value);
      bool GetBit(int number);
   public:
      TLaier();
      TLaier(const AnsiString _laier);
      TLaier(const AnsiString* _laier);
      TLaier(const TLaier& _laier);
      TLaier(const TLaier* _laier);
      ~TLaier(){};
      bool  operator==(const TLaier& _other)const;
      bool  operator==(const TLaier* _other)const;
      bool  operator!=(const TLaier& _other)const;
      bool  operator!=(const TLaier* _other)const;
      bool  Identical(const TLaier& _other)const;
      bool  Identical(const TLaier* _other)const;
      TLaier& operator=(const TLaier& _other);
      TLaier& operator=(const TLaier* _other);
      TLaier& operator+=(/*const*/ TLaier& _other);
      TLaier& operator+=(/*const*/ TLaier* _other);
      bool operator&(/*const*/ TLaier& _other);
      bool operator&(/*const*/ TLaier* _other);
      void AddBit(void);
      void DeleteBit(int number);
      int AnsiPos(const AnsiString& SubStr);
      int AnsiPos(const AnsiString* SubStr);
      void Write(TFileStream* fs);
      void Read(TFileStream* fs);
      void SaveToFile(AnsiString FileName);
      void LoadFromFile(AnsiString FileName);
      __property  bool  Bit[int item] = {read=GetBit,write=SetBit};
      __property  AnsiString Dato = {read=FDato,write=FDato};
      __property unsigned int Handler = {read=fhandler};
};
#endif
//--------------------------------------------------------------------------
-
//TLaier.cpp
#include <vcl.h>
#pragma hdrstop
#include "TLaier.h"
//--------------------------------------------------------------------------
-
#pragma package(smart_init)
//fhandler = reinterpret_cast<unsigned int>(this);
TLaier::TLaier()
{
   FDato = "";
   fhandler = reinterpret_cast<unsigned int>(this);
}
//--------------------------------------------------------------------------
------
TLaier::TLaier(const AnsiString _laier)
{
   FDato = _laier;
   fhandler = reinterpret_cast<unsigned int>(this);
}
//--------------------------------------------------------------------------
------
TLaier::TLaier(const AnsiString* _laier)
{
   FDato = *_laier;
   fhandler = reinterpret_cast<unsigned int>(this);
}
//--------------------------------------------------------------------------
------
TLaier::TLaier(const TLaier& _laier)
{
   FDato = _laier.Dato;
   fhandler = reinterpret_cast<unsigned int>(this);
}
//--------------------------------------------------------------------------
------
TLaier::TLaier(const TLaier* _laier)
{
   FDato = _laier->Dato;
   fhandler = reinterpret_cast<unsigned int>(this);
}
//--------------------------------------------------------------------------
------
bool TLaier::operator==(const TLaier& _other)const
{
   return FDato == _other.Dato;
}
//--------------------------------------------------------------------------
------
bool TLaier::operator==(const TLaier* _other)const
{
   return *this == *_other;
}
//--------------------------------------------------------------------------
------
bool TLaier::operator!=(const TLaier& _other)const
{
   return !(*this == _other);
}
//--------------------------------------------------------------------------
------
bool TLaier::operator!=(const TLaier* _other)const
{
   return !(*this == *_other);
}
//--------------------------------------------------------------------------
------
bool TLaier::Identical(const TLaier& _other)const
{
   return fhandler == _other.Handler;
}
//--------------------------------------------------------------------------
------
bool TLaier::Identical(const TLaier* _other)const
{
   return fhandler == _other->Handler;
}
//--------------------------------------------------------------------------
------
TLaier& TLaier::operator=(const TLaier& _other)
{
   FDato = _other.Dato;
   fhandler = reinterpret_cast<unsigned int>(this);
   return *this;
}
//--------------------------------------------------------------------------
------
TLaier& TLaier::operator=(const TLaier* _other)
{
   FDato = _other->Dato;
   fhandler = reinterpret_cast<unsigned int>(this);
   return *this;
}
//--------------------------------------------------------------------------
------
TLaier& TLaier::operator+=(/*const*/ TLaier& _other)
{
   for(int i = 1 ; i < FDato.Length() ; i++)
   {
      if(FDato[i] == '0' && _other.Dato[i] == '0') FDato[i] = '0';
      if(FDato[i] == '1' && _other.Dato[i] == '0') FDato[i] = '1';
      if(FDato[i] == '1' && _other.Dato[i] == '1') FDato[i] = '1';
      if(FDato[i] == '0' && _other.Dato[i] == '1') FDato[i] = '1';
   }
   return *this;
}
//--------------------------------------------------------------------------
------
TLaier& TLaier::operator+=(/*const*/ TLaier* _other)
{
   for(int i = 1 ; i < FDato.Length() ; i++)
   {
      if(FDato[i] == '0' && _other->Dato[i] == '0') FDato[i] = '0';
      if(FDato[i] == '1' && _other->Dato[i] == '0') FDato[i] = '1';
      if(FDato[i] == '1' && _other->Dato[i] == '1') FDato[i] = '1';
      if(FDato[i] == '0' && _other->Dato[i] == '1') FDato[i] = '1';
   }
   return *this;
}
//--------------------------------------------------------------------------
------
bool TLaier::operator&(/*const*/ TLaier& _other)
{
   if(FDato.Length() < _other.Dato.Length())
   {
      for(int i = 1 ; i <= FDato.Length() ; i++)
      {
         if(FDato[i] == '1' && _other.Dato[i] == '1') return true;
      }
   }
   else
   {
      for(int i = 1 ; i <= _other.Dato.Length() ; i++)
      {
         if(FDato[i] == '1' && _other.Dato[i] == '1') return true;
      }
   }
   return false;
}
//--------------------------------------------------------------------------
------
bool TLaier::operator&(/*const*/ TLaier* _other)
{
   if(FDato.Length() < _other->Dato.Length())
   {
      for(int i = 1 ; i <= FDato.Length() ; i++)
      {
         if(FDato[i] == '1' && _other->Dato[i] == '1') return true;
      }
   }
   else
   {
      for(int i = 1 ; i <= _other->Dato.Length() ; i++)
      {
         if(FDato[i] == '1' && _other->Dato[i] == '1') return true;
      }
   }
   return false;
}
//--------------------------------------------------------------------------
------
int TLaier::AnsiPos(const AnsiString& SubStr)
{
    return FDato.AnsiPos(SubStr);
}
//--------------------------------------------------------------------------
-------
int TLaier::AnsiPos(const AnsiString* SubStr)
{
   return FDato.AnsiPos(*SubStr);
}
//--------------------------------------------------------------------------
-------
void TLaier::AddBit(void)
{
   FDato += "0";
}
//--------------------------------------------------------------------------
------
void TLaier::DeleteBit(int number)
{
   AnsiString s1,s2;
   s1 = FDato.SubString(0,number-1);
   s2 = FDato.SubString(number+1,FDato.Length());
   FDato = s1 + s2;
}
//--------------------------------------------------------------------------
------
void TLaier::SetBit(int number,bool value)
{
   if(value == true)
      FDato[number] = '1';
   else
      FDato[number] = '0';
}
//--------------------------------------------------------------------------
------
bool TLaier::GetBit(int number)
{
   if(FDato[number] == '1')
      return true;
   else if(FDato[number] == '0')
      return false;
   else
      return false;
}
//--------------------------------------------------------------------------
------
void TLaier::Read(TFileStream* fs)
{
   char* s;
   int number;
   fs->Read(&number , sizeof(int));
   if(number > 0)
   {
      s = new char[number + 1];
      memset(s , 0 , number);
      fs->Read(s , number);
      s[number] = 0;
      FDato = s;
      delete []s;
   }
   else
      FDato = "";
}
//--------------------------------------------------------------------------
------
void TLaier::Write(TFileStream* fs)
{
    char* s;
    int number;
    number = FDato.Length();
    fs->Write(&number , sizeof(int));
    if(number > 0)
    {
        s = new char[number + 1];
        memset(s , 0 , number);
        strncpy(s , FDato.c_str() , number);
        s[number] = 0;
        fs->Write(s , number);
        delete []s;
    }
}
//--------------------------------------------------------------------------
------
void TLaier::SaveToFile(AnsiString FileName)
{
    TFileStream* fs = new TFileStream(FileName,fmCreate);
    Write(fs);
    delete fs;
}
//--------------------------------------------------------------------------
------
void TLaier::LoadFromFile(AnsiString FileName)
{
    TFileStream* fs = new TFileStream(FileName,fmOpenRead);
    Read(fs);
    delete fs;
}
//--------------------------------------------------------------------------
------

 > almeno di cosa vorrebbe fare e perche').
Ancora non capisco per quale motivo il layer dovrebbe ridefinire la classe A
(che nel tuo caso credo sia TCustomPoint2D).
Comunque visto il tipo di progetto che stai affrontando ti consiglio di
analizzare molto bene cosa e' una classe (cioe' un oggetto con interfacce e
tutte quelle cose li) e cosa vuoi implementare a basso livello (ovvero come
semplice tipo).
La mia esperienza mi dice che in un sistema CAD/CAM conviene spesso avere dei
tipi fondamentali da trattare alla stregua di int e long anche se sono in
realta' delle classi. Compresi per esempio i numeri complessi (che in C++ fanno
parte della libreria) i punti ed i vettori (nelle loro varie forme: 2D, 3D,
cartesiani, omogenei etc.), e le relative operazioni aritmetiche. Poi una
differente ma simile classe rappresenta l'uso dei punti/vettori a livello
specifico (vertici, normali etc.). Esattamente come "age" e' un "int", "vertex"
e' un "point". E esattamente come puo' succedere di avere in certi sistemi la
"class Age" che internamente avra' da qualche parte un "int" o che comunque lo
potra' ritornare, avrai la "class Vertex" che internamente terra' un "point" o
lo potra' ritornare.
Se gli oggetti a basso livello sono ben scritti non dovrebbe verificarsi mai il
caso di doverli modificare. E quando si presenta puoi sempre usare un wrapper di
qualche genere (pattern adapter, per esempio).


Scusa mi era sfuggito di omettere tutti i body e' che stavo scrivendo e
programmando, cosi' da un passaggio all'altro, da una finestra all'altra mi
e' sfuggito l'invia, ma oramai era andato (poteri della adsl).
Bene il problema non e' di modificare la classe TCustomPoint2D ma di
estenderla dinamicamente a seconda del progetto alla quale la applico.
Quello che ho voluto cercare di fare era una classe nella quale fossero
separati i dati da salvare da quelli locali della classe messi insieme ai
metodi. E fino a li' ho risolto con la classe TDB................, ora sto
cercando un sistema per agganciare qualcosa alla mia classe affinche io
possa immettere di volta in volta le personalizzazioni del mio progetto, il
che vuol dire che questo innesto potrebbe essere vuoto o stracolmo di altri
piccoli oggetti molto specializzati. Il tutto senza dover riprogettare la
libreria ma solo scrivendo il codice specialistico e ricompilando. Cosi'
facendo voglio poter ereditare tutti i dati nelle classi derivate composte
dalla collezione di TCustomPoint2D. Quello che voglio ottenere e' di poter
applicare queste nuove caratteristiche ai nodi che sono le entita' piu'
piccole della libreria. Cio' non vuol dire che debba caricare tutti i
progetti con questi dati pensati e studiati per una particolare
applicazione, ma solo la' dove sia necessario. Nel medesimo modo vorrei fare
la medesima cosa con TCustomBaseLine2D che e' la classe base per le linee
complesse, questo se in una applicazione non ho necessita' di controllare il
singolo nodo ma possa accontentarmi di avere il dominio delle linee.
Penso che tu abbia capito quanto sono esaurito vero?
Ciao Angelo.
"Andrea Ferro" <And...@UrkaDVD.it> ha scritto nel messaggio
news:969ab8$64m$1...@serv1.iunet.it...


 piccoli oggetti molto specializzati. Il tutto senza dover riprogettare la
Prova a rileggerti i pattern classici. Come decorator, adapter e forse altri.
Se ho ben capito potrebbero essere la risposta al tuo problema o darti qualche
idea.


metodi dell'oggetto.
il problema è sempre lo stesso: le modifiche devono essere fatte a
tempo di compilazione o runtime? da quello che hai scritto mi sembra
di capire durante la compilazione, quindi possiamo tranquillamente
usare i template. Guarda se va bene qualcosa del genere
class DatiA { /*...*/ };
class default_Undef { }; // classe vuota
// il template ha un argomento di default, così è possibile istanziare
// oggetti di tipo A senza specificare Undef
template<class Undef=default_Undef> class A: public Undef { /*... */};
template<class Undef=default_Undef> class DatiB {
	container< A<Undef> > contenitore_di_A;
	/* ... */
};
e poi segui lo stesso procedimento per definire B, DatiC e C.
A questo punto un programmatore può scrivere:
class my_DatiA { /*...*/ };
class Oggetto: public C<my_DatiA> { /*...*/ };
a questo punto puoi tranquillamente usare le classi A,B,C senza
specificare il template e estenderle quando occorrono. Cmq mi sembra
una tecnica abbastanza complicata: sei sicuro che ti serva veramente?
Inoltre, hai considerato la possibilità di incapsulare DatiA in A,
invece di derivare:
class A {
	DatiA miei_dati;
};
non ci ho pensato a fondo, ma forse è più semplice.
Ciao Anto


 ha scritto nel messaggio
news:7QOJOjo8FWvXMi...@4ax.com...


Grazie Angelo
prego, Anto
PS: potresti correggere il tuo newsreader per quotare all'inizio del
messaggio invece che alla fine? Questa è la prassi più comune.



Scusa non e' questione di news ma della mia mano.
Ok quello che intendevo era di poter creare un oggetto che esegue una certa
funzionalita' e agganciarlo alla libreria attraverso il template. Cosi' da
poter definire un oggetto che ha un certo comportamento e una sua
funzionalita'. Questo oggetto potra' essere diverso per ogni applicazione o
applicativo si voglia progettare con la libreria, o non esistere se le
funzionalita' della libreria sono sufficenti. Cuaramente ogni progetto avra'
un solo oggetto, e questo non sara' diverso per ogni chiamata degli oggetti
della libreria, anche se esso e' possibile.
Be ok grazie ciao
Appena fatte le modifiche e provate postero' la libreria aggiornata e ti
faro' sapere.
Cio Angelo