Newsgroup: it.comp.lang.c++
Titolo: 2 domande su C++

Salve,
 ho 2 quesiti sul c++ da sottoporre alla VS attenzione.
Il primo č:
se all'interno di una funzione dichiaro una mia classe MyString. che viene
ritornata dalla funzione cosa fa il compilatore?
Sono dell'idea che in c++ valga il seguente ragionamento:
MyString foo() {
  MyString str;
  str = "questa č una stringa dinamica";
  return str;
}
MyString s = foo();
Nella funzione foo()  viene istanziata la classe Mystring nello stack. Viene
richiamato il costruttore di default. l'operarore = fa una new per contenere
la stringa. La memoria allocata dalla new va a finire nello heap. Nella
return viene creato un nuovo oggetto temporaneo tramite il costruttore di
copia della MyString, viene chiamato il distruttore di str e l'oggetto viene
distrutto dallo stack
All'esterno l'istruzione s = foo()  fa una nuova copia con conseguente
chiamata del costruttore di copia e l'oggetto temporaneo viene distrutto.
Ma se questo oggetto temporaneo venisse veramente creato il compilatore dove
lo alloca? Nello heap? Perchč se fosse stato allocato nello stack
all'esterno della routine non sarebbe piů visibile.
Bč ho le idee un po' confuse e non so se ci sono andato vicino o no!
Il secondo quesito č:
Il supporto al RTTI del c++ mi consente di sapere di che tipo č una classe?
Cioč se č una
classe base o derivata?  In java si puň fare. Su winzoz ad esempio quelli
della microsoft si sono fatti una classe cobject che mantiene traccia di
queste informazioni.Ma non č standard.
Grazie

 queste informazioni.Ma non è standard.
Si che si può fare, la RTTI serve proprio a quello. Puoi ad esempio usare
l'operatore dynamic_cast:
#include <iostream>
class A {};
class B: public A {};
int main() {
 B b;
 if (dynamic_cast<A*>(&b)) std::cout << "b e` un A" << std::endl;
}
Ciao

 classe?
Non è meglio se prima ti studi cosa dice il tuo manuale a proposito di RTTI e
poi eventualmente chiedi chiarimenti?
Max


Visto che ti hanno gia' risposto a questa domanda, voglio aggiungere solo un
consiglio. Credo sia meglio evitare di far passare da una funzione una
classe, oppure una struttura. Meglio creare un oggetto attraverso l'uso di
new e poi far ritornare un puntatore all'oggetto creato. Risparmi memoria e
il codice e' piu' veloce.


Ipotesi.
Un sistema RTOS non troppo robusto, un thread puo' venire interrotto proprio
mentre questo sta ritornando una classe particolarmente pesante. In queste
condizione potrebbe uscire fuori anche un segmentation fault anche se
concettualmente il codice e' corretto. Errore del sistema operativo dirai
tu, ma in ogni caso le necessita' cambiano in base ai contesti di lavoro.
Aggiungo che il costruttore di copia oppure overriding dell'operatore =
sono sempre essere molto utili, ma quando si hanno due oggetti che vivono e
che continuano a vivere nello stesso momento. Questo non e' il caso di un
oggetto che viene ritornato da una funzione.
Ciao
Emanuele.

 il codice e' piu' veloce.
Scusa, ma ti riferisci in generale? Cioč, per te č meglio che una funzione
se deve ritornare un oggetto lo carichi sempre nello heap?
Ciao

 Una delle meraviglie dell'OO è proprio questo ....
No, l'OO serve a sfruttare le possibilità che ti offre l'OOP, non ad evitare
di programmare dinamicamente, quello in C/C++ esisterà sempre.
Ciao

 metodologia, gli sarei grato se me lo dice.
Non trovo nessun difetto, trovo solamente che Java non è C++. In particolare
la gestione della memoria in Java è gestita dal garbage collector, per cui
ti puoi permettere di usare new dove cavolo ti pare, ma in C++ non lo puoi
fare, perchè ad ogni new deve corrispondere una delete, e se usi new a
casaccio voglio vedere poi come fai ad evitare un memory leak.
Ciao

 oggetto che viene ritornato da una funzione.
Non ho capito a quale conclusione vuoi arrivare con questa considerazione.
Ciao

...

Non condivido il tuo consiglio: l'allocazione di memoria tramite new
e' un'operazione complessa, sul cui consumo di memoria e di CPU non
farei assunzioni avventate. Prima di procedere nella direzione che
suggerisci sarebbe opportuno verificare un paio di cose:
1) che la soluzione presentata, che e' semplice e pulita, rappresenti
effettivamente un problema prestazionale.
2) che l'allocazione dinamica della stringa comporti effettivamente un
beneficio prestazionale che compensi i problemi di gestione che
introduce.
Ciao,
Nicola

 Ciao
scusami ma non mi trovo!
perchč se io ho un ulteriore livello di derivazione non funziona piů il
tuo tuo esempio:
 class A {};
 class B: public A {};
 class C: public B {};
int main() {
  C b;
  if (dynamic_cast<B*>(&b)) std::cout << "C e` un B" << std::endl;
  if (dynamic_cast<A*>(&b)) std::cout << "C e` un A" << std::endl;
}
E se nella mia funzione l'argomento passato all'operatore
di cast č lo stesso del tipo castato? Cioč:
void foo(C * c) {
 if (dynamic_cast<C*>(c)) std::cout << "Non compila" << std::endl;
}
e se passassi una classe che non appartiene alla mia gerarchia di
derivazione?
non mi compila neppure! Mentre a me servirebbe determinare il tipo di un
oggetto
a runtime come in java tramite la parola chiave  "instance of"!
-- 
Posted via Mailgate.ORG Server - 
http://www.Mailgate.ORG

Ipotesi.
La tua ipotesi, con tutto il rispetto, non č accettabile e messa in
questi termini porta ad un'unica conclusione: NESSUN sistema č
affidabile (il che non č ovviamente vero).  
-- 
Andrea Laforgia - a.laforgia(at)
libero.it
"There are three kinds of people in this world; 
 those who are good at maths and those who aren't".

"Nicola Musatti" <Nicola....@ObjectWay.it> ha scritto nel messaggio
news:a327cf48.03071...@posting.google.com...

Suddivido in punti la risposta
1) Da quanto ho capito il problema non era legato ad una stringa, ma ad un
oggetto non ben identificato (l'esempio era solo un esempio).
2) con oggetti grandi crearne due (ossia scrivere pippo = pluto) e' oneroso
più di farne uno (dinamico) e riportare il puntatore. Credo che ricavare un
1MB di memoria sull'heap sia meno oneroso di crearne due sullo stack per poi
copiare i dati da una allocazione all'altra. Se poi lavori su un processore
embedded certe questioni diventano ancora più pressanti.
3) Ogni caso comunque fa testo a se. Se le prestazioni non sono un problema
e gli oggetti sono piccoli si può evitare di usare new e delete.
4) L'uso di new  e delete è comunque da non scoraggiare. Come ho già detto
(ma il messaggio è stato postato da un server del cavolo e ancora non si
vede) evitare di usare l'allocazione dinamica è un errore perchè in alcuni
ambiti non puoi proprio farne a meno, e saperla usare è un'ottima arma da
battaglia.
Come ho già detto mi riferisco ad oggetti abbastanza grandi rispetto alla
potenza del computer e anche a situzioni in ambiente Real Time (RTOS).
Ciao
Emanuele

 affidabile (il che non è ovviamente vero).
ARGHHHH!!!
Con tutto rispetto credo che dovresti conoscerli i sistemi RTOS. Quando il
timer dice di fare una cosa, il sistema operativo la fa e poco gliene frega
di quello che succede in giro, specie se alcuni thread hanno priorità molto
elevata. Inoltre spesso non ci sono protezioni di memoria e gli
incagliamenti del SO sono quanto mai frequenti (io lavoro con Lynxos 4.0 e
2.5). Per quanto riguarda l'ipotesi che ho fatto, è stato un problema
successo davvero, quindi c'è veramente poco da discutere. Su un sistema
windows o linux non sarebbe ovviamente successo. Su un sistema RTOS devi
calcolare bene la temporizzazione dei task altrimenti va tutto in palla.
Ovviamente è difficile che una metta questi sistemi operativi sul computer
di casa.
Il che significa che la tua "unica conclusione" non è unica. Esistono
sistemi diversi da altri.


Per inciso parlo di oggetti abbastanza grandi.
Ritengo che costruire un oggetto, costruire un'atro oggetto e copiarlo (ho
quindi costruito contemporaneamente due oggetti)  e poi distruggerne uno
all'uscita della funzione  e' piu' oneroso che costruirne uno solo e poi
passare un puntatore a quest'ultimo. Se una funzione e' incaricata di
costruire un oggetto e ritornarlo, e' plausibile che un passare un puntatore
ad oggetto piuttosto che due oggetti sia piu' efficiente. In ambienti RTOS
ritornare grandi strutture di dati e' una iattura.
Ciao

 casaccio voglio vedere poi come fai ad evitare un memory leak.
Ho preso come esempio Java perche' e' un linguaggio ad oggetti, e volevo
porre l'attenzione che i passaggi per puntatore non sono contrari alla
programmazione OO.
Ovviamente hai ragione a porre l'attenzione al Garbage Collector, il quale
non e' tuttavia il nodo centrale sull'uso dei puntatori o riferimenti.
Per quanto riguarda l'uso di new e delete contemporaneamente, credo che il
loro uso vada pesato e ben gestito, ma se si rinuncia troppo presto ad usare
l'allocazione dinamica in C++ tanto vale usare un'altro linguaggio piu'
semplice.
Ciao


Dicevo che ritornare da una funzione un puntatore ad oggetto, piuttosto che
una copia dell'oggetto stesso, non significa che si stanno svalutando le
caratteristiche del C++ come l'overriding dell'operatore = o del costruttore
di copia, in quanto queste caratteristiche sono state pensate per molte
altre situazioni anche piu' frequenti. (Leggi il post precedente al mio)

 ha scritto nel
messaggio news:bem7l0$ioe$1...@e3k.asi.ansaldo.it...

Ok, sono d'accordo che copiare un oggetto grosso sia oneroso; non sono certo
perň che allocare dinamicamente un oggetto sia poi cosě economico, mentre
sono sicuro che porti piů grattacapi al programmatore ;-)
Ciao

 a runtime come in java tramite la parola chiave  "instance of"!
Molte cose che in Java sono immediate non lo sono in C++.
Comunque l'operatore dynamic_cast serve a due scopi:
- convertire un puntatore ad un oggetto ad un altro puntatore di una classe
della stessa gerarchia: se il cast fallisce il risultato è il puntatore
NULL;
- convertire un lvalue di una classe ad un riferimento ad una classe della
stessa gerarchia: in caso di fallimento viene sollevata l'eccezione
bad_cast.
In genere l'operatore dynamic_cast si usa per fare un downcast sicuro:
supponi di avere due classi derivate dalla stessa classe base che
differiscano per un dato metodo:
void foo(Base *b) {
    if (dynamic_cast<Der1*>(b)) b->foo1();
    else if (dynamic_cast<Der2*>(b)) b->foo2();
}
ad esempio puoi chiamare la funzione in questo modo:
Der1 d1;
Der2 d2;
Altra a;
foo(&d1); // chiama d1.foo1()
foo(&d2); // chiama d2.foo2()
Ciao

 ritornare grandi strutture di dati e' una iattura.
Sinceramente mi pare che "ritengo" ed "č plausibile" siano un po' poco
per dare validitą ad un affermazione simile...
Una funzione che restituisce un puntatore crea sicuramente complicazioni
e possibili problemi, ed il suo utilizzo, che non č ovviamente da
scartarsi a priori, deve essere attentamente valutato e testato.
Ciao.
-- 
BSD is for UNIX lovers -- linux is for Windows haters.


Ne crei due di oggetti, non uno, c'e' anche un costo di memoria doppio.
Considera anche i costi di memoria.
E poi siamo programmatori o caporali?
Ciao
Emanuele

 Emanuele
Ciao