PDA

Visualizza la versione completa : [C++] Istanziare un oggetto


fbcyborg
12-12-2011, 16:35
In riferimento alla vecchia discussione [C++] Istanziare un oggetto (http://forum.html.it/forum/showthread.php?s=&postid=13506306#post13506306) ringrazio oregon per avermi dato la sua risposta ma il mio dubbio comunque rimane. Il concetto di puntatore credo di averlo più che chiaro, anche se forse sembra di no.
Provengo da un lungo periodo di programmazione in Java, quindi ho sempre istanziato un oggetto in questo modo:

Oggetto o = new Oggetto();

Inoltre so che se in Java voglio referenziare un oggetto faccio Oggetto o1 = o. Così o1 punta ad o (se vogliamo metterla in termini di puntatori).

A quanto capisco, in c++ ci sono due (o più?) modi per istanziare un oggetto, ma non capisco quando va usato uno e quando l'altro. Ho capito "solo se ho un puntatore a un oggetto", ma ad esempio, se ho la seguente classe:

Persona.cpp


#include "Persona.h"

string nome;
string cognome;

Persona::Persona(string n, string c) {
this->nome=n;
this->cognome=c;
}

string Persona::get_nome(){
return nome;
}

string Persona::get_cognome(){
return cognome;
}

Persona::~Persona() {}

e Persona.h è così definito:


#ifndef PERSONA_H_
#define PERSONA_H_

#include <string>

using namespace std;

class Persona {
private:
string nome;
string cognome;
public:
Persona(string n, string c);
string get_nome();
string get_cognome();
virtual ~Persona();
};

#endif /* PERSONA_H_ */

Se voglio testare la classe Persona, creo un file main.cpp con il seguente contenuto:


#include "objects/Persona.h"
#include <iostream>

int main(){

Persona* p = new Persona("Pippo", "Pluto");
cout << "Nome: " << p->get_nome() << endl;
cout << "Cognome: " << p->get_cognome() << endl;

}

Ora la domanda è: sì fa sempre così? Si usa sempre l'operatore new? Ci sono altri modi?
Quando si fa in un modo piuttosto che in un altro?

Grazie.

oregon
12-12-2011, 18:21
In C++, puoi creare una istanza di una classe scrivendo

Persona persona;

L'istanza sarà creata a runtime richiamando il costruttore nel momento in cui verrà eseguita quella riga. Potrai quindi usare proprietà e metodi, ad esempio

persona.metodo();

Se usi un puntatore come

Persona *pPersona;

non avrai una istanza fino a che non esegui una

pPersona = new Persona();

Da questo momento avrai un puntatore all'istanza che potrai usare con

pPersona->metodo();

Per i dettagli, è meglio dare una buona lettura ad un libro di C++ dato che in un forum non è possibile fare molto di più ...

fbcyborg
12-12-2011, 18:30
Originariamente inviato da oregon
In C++, puoi creare una istanza di una classe scrivendo

Persona persona;

[...]

Persona *pPersona;

non avrai una istanza fino a che non esegui una

pPersona = new Persona();

OK, quindi sostanzialmente ci sono due modi equivalenti, mi pare di capire.


Originariamente inviato da oregon
Per i dettagli, è meglio dare una buona lettura ad un libro di C++ dato che in un forum non è possibile fare molto di più ...
È proprio quello che sto facendo. Ho sottomano un buon manuale che ho comprato in formato cartaceo (e sto parlando di Thinking in C++), e lo sto studiando. Proprio perché sto studiando su questo manuale mi sono sorti dei dubbi e proprio per questo sto chiedendo al fine di capire meglio.
Ed il dubbio che mi era rimasto, per l'appunto, era su quando usare l'uno o l'altro metodo.

Grazie ancora per l'aiuto.

EDIT: ecco infatti ho provato a testare la classe Persona in questo modo, e funziona ugualmente:

Persona p("Pippo","Pluto");
cout << "Nome: " << p.get_nome() << endl;
cout << "Cognome: " << p.get_cognome() << endl;
Per questo non capisco cosa mi cambia nell'usare questo metodo piuttosto che l'altro visto che ottengo lo stesso risultato.

LeaderGL
12-12-2011, 22:01
Ciao,
ci sono sostanziali differenze nei due modi di istanziare una classe (le classi si istanziano, mentre gli oggetti sono istanze di una classe) in C++.

Data una classe siffatta:


class Persona
{
protected:
char *sNome;
char *sCognome:
unsigned short iEta;

public:
Persona();
Persona(const char *nome, const char *cognome);
virtual ~Persona();
}


i due modi per istanziarla sono:


int main(void)
{
Persona oP1;

Persona *pP1 = NULL;
pP1 = new Persona();

return EXIT_SUCCESS;
}


Nel primo caso (oP1) viene immediatamente creato un oggetto di tipo "Persona". Questo oggetto viene allocato nello stack ed appena si esce dallo scope in cui è definito verrà automaticamente invocato il distruttore dell'oggetto (~Persona).

Nel secondo caso (pP1) all'atto della sua dichiarazione non succede nulla, mentre non appena sarà effettuata la "new" verrà allocata dinamicamente della memoria nello heap ed in "pP1" verrà memorizzato l'indirizzo di memoria relativo (il puntatore all'area dello heap). Questo oggetto NON viene automaticamente distrutto quando si esce dallo scope in cui è definito, va quindi utilizzato l'operatore "delete".

E' bene definire un distruttore per ogni classe che si crea, sopratutto se questa classe alloca dinamicamente della memoria (come in questo caso, il costruttore di "Persona" dovrà allocare memoria per "nome" e "cognome" al fine di poterli utilizzare).

fbcyborg
12-12-2011, 22:11
LeaderGL GRAZIE!

Sei stato chiarissimo. Ora ho capito perfettamente la differenza.
Grazie anche per la precisazione sul fatto che si istanzia una classe e non l'oggetto.

Quindi sostanzialmente, utilizzo il secondo modo (*pP1), quando c'è la necessità di dichiarare un oggetto, una variabile, che però non voglio "istanziare" subito ma della quale voglio fare la dichiarazione ad inizio codice.

Ad occhio direi che sia più conveniente il primo modo (op1), dal momento che viene invocato il distruttore in automatico, ma suppongo non sia questa la ragione principale per cui uno scelga fra un modo e l'altro.

Grazie ancora!

LeaderGL
12-12-2011, 22:27
Figurati, non c'è di che.

L'utilizzo di un metodo piuttosto di un altro dipende da quello che ci devi fare.

Alcuni esempi:

per realizzare una "collezione" di oggetti devi allocarli in maniera non temporanea (ti serve quindi "new" ed i puntatori);

per realizzare un software complesso, strutturato e che gestisca degli "stati" in evoluzione devi allocare oggetti in maniera non temporanea;

se ti serve un oggetto per gestire particolari operazioni su un set di dati non necessariamente ti serve utilizzare i puntatori.


Brevi esempi di codice possono essere:


vector<Persona *> vCustomerList;

void AddCustomer(Persona *customer);

int main(void)
{
Persona *pP1 = new Persona();
Persona *pP2 = new Persona();

AddCustomer(pP1);
AddCustomer(pP2);
}

void AddCustomer(Persona *customer)
{
vCustomerList.insert (vCustomerList.begin(), customer);
}


Come puoi vede la variabile globale che gestisce la lista è un "oggetto globale", non lo abbiamo realizzato tramite i puntatori.

Mentre gli elementi che contiene sono gestiti tramite oggetti allocati dinamicamente, questo per fare in modo che quando si esce dalle funzioni di gestione quegli oggetti si perdano.

Ci sono tante altre motivazioni e modi d'uso, ma questi rendono l'idea.

fbcyborg
12-12-2011, 22:36
Perfetto, è proprio quello che volevo capire!

:)

Loading