Visualizzazione dei risultati da 1 a 10 su 10
  1. #1
    Utente di HTML.it L'avatar di MrX87
    Registrato dal
    Jun 2007
    Messaggi
    500

    [C++] Chiarimento su costruttore oggetto

    Ciao a tutti, volevo un semplice chiarimento sull'implementazione di un semplice costruttore in c++, scrivo sotto un esempio:

    objectA.j
    codice:
    class ObjectA {
        public:
           ObjectA(string mystr);
           ~ObjectA();
        private:
           string mystr;
    }
    objectB.h
    codice:
    class ObjectB {
        public:
           ObjectB();
           ~ObjectB();
        private:
           ObjectA a1;
    }
    objectB.cpp
    codice:
    ObjectB::ObjectB() {
        a1 = ObjectA("Mario"); // è una operazione valida come chiamata al costruttore???
    }
    "Non può piovere per sempre" Il Corvo
    Forza Vigor!

  2. #2
    No. Se vuoi richiamare un costruttore (di campi o di classi base) da un costruttore devi usare la sintassi della lista di inizializzazione. Quello che stai facendo lì è creare un ObjectA temporaneo e assegnarlo ad a1 tramite operatore di assegnamento.

    (per inciso, quel codice non potrebbe compilare, dato che ObjectA non ha costruttore di default, e non ne richiami il costruttore parametrico)
    Amaro C++, il gusto pieno dell'undefined behavior.

  3. #3
    Utente di HTML.it L'avatar di MrX87
    Registrato dal
    Jun 2007
    Messaggi
    500
    Grazie per la risposta e la correzione, comunque se dichiarassi invece:

    codice:
    private:
         ObjectA *a1;
    A questo punto lo inizializzo con la new:

    codice:
    a1 = new Object("Mario");
    dovrebbe funzionare...e ovviamente dovrei prevedere una delete a1 nel distruttore di ObjectB giusto?
    Ultima modifica di MrX87; 20-05-2014 a 08:35
    "Non può piovere per sempre" Il Corvo
    Forza Vigor!

  4. #4
    Sì, ma se inizi ad avere oggetti tenuti dentro per puntatore devi pensare ad una semantica di copia sensata, o quantomeno disabilitare costruttore di copia e operatore di assegnamento, altrimenti con il costruttore di copie di default ti ritrovi più ObjectB che puntano allo stesso a1 e cercano tutti di distruggerlo alla loro distruzione (con risultati catastrofici).
    In generale, se possibile è più semplice avere membri "veri" e non puntatori a roba allocata sull'heap.
    Amaro C++, il gusto pieno dell'undefined behavior.

  5. #5
    Utente di HTML.it L'avatar di MrX87
    Registrato dal
    Jun 2007
    Messaggi
    500
    Quote Originariamente inviata da MItaly Visualizza il messaggio
    Sì, ma se inizi ad avere oggetti tenuti dentro per puntatore devi pensare ad una semantica di copia sensata, o quantomeno disabilitare costruttore di copia e operatore di assegnamento, altrimenti con il costruttore di copie di default ti ritrovi più ObjectB che puntano allo stesso a1 e cercano tutti di distruggerlo alla loro distruzione (con risultati catastrofici).
    In generale, se possibile è più semplice avere membri "veri" e non puntatori a roba allocata sull'heap.
    Suppongo che quello che hai appena spiegato possa accadere in una situazione del genere se non ho capito male:
    ObjectA
    codice:
    #ifndef OBJECTA_H
    #define OBJECTA_H
    
    class ObjectA
    {
        public:
        int y;
            ObjectA();
            ~ObjectA();
    };
    #endif // OBJECTA_H
    ObjectA.cpp
    codice:
    #include <iostream>
    #include "ObjectA.h"
    
    ObjectA::ObjectA()
    {
        y = 10;
        std::cout << "Costruttore A " << std::endl;
    }
    
    ObjectA::~ObjectA()
    {
    }
    ObjectV
    codice:
    #ifndef OBJECTV_H
    #define OBJECTV_H
    
    #include "ObjectA.h"
    
    class ObjectV
    {
        public:
            int x;
            ObjectV();
            ~ObjectV();
            ObjectA* getObjA();
    
    
        private:
            ObjectA* a1;
    };
    
    
    #endif // OBJECTV_H
    ObjectV.cpp
    codice:
    #include <iostream>
    #include "ObjectV.h"
    
    ObjectV::ObjectV() 
    {
        std::cout << "Costruttore V " << std::endl;
        a1 = new ObjectA();
    }
    
    ObjectV::~ObjectV()
    {
        delete a1;
    }
    
    
    ObjectA*  ObjectV::getObjA() {
        return a1;
    }
    main
    codice:
    #include <iostream>
    #include "ObjectA.h"
    #include "ObjectV.h"
    
    int main (void) {
    
    
        ObjectV v1;
        ObjectV v2 = v1;
    
    
        ObjectA* av1 = v1.getObjA();
        std::cout << "address a->y =  " << av1->y << std::endl;
        delete av1;
    
    
        ObjectA* av2 = v2.getObjA();
        std::cout << "address a->y =  " << av2->y << std::endl; // in questo caso si av2 non esiste più poichè distrutto dalla delete di av1.
    
    
        getchar();
        return 0;
    }

    anche se un po estremizzata come situazione però il concetto penso è questo....
    "Non può piovere per sempre" Il Corvo
    Forza Vigor!

  6. #6
    No, il concetto è molto più semplice.
    codice:
    int main()
    {
        ObjectV v1;
        ObjectV v2(v1);
        return 0;
    }
    Crasha (se sei fortunato) perché sia il distruttore di v1 che quello di v2 stanno cercando di eliminare lo stesso oggetto (quello a cui puntano entrambi gli a1).
    Amaro C++, il gusto pieno dell'undefined behavior.

  7. #7
    Utente di HTML.it L'avatar di MrX87
    Registrato dal
    Jun 2007
    Messaggi
    500
    Quote Originariamente inviata da MItaly Visualizza il messaggio
    No, il concetto è molto più semplice.
    codice:
    int main()
    {
        ObjectV v1;
        ObjectV v2(v1);
        return 0;
    }
    Crasha (se sei fortunato) perché sia il distruttore di v1 che quello di v2 stanno cercando di eliminare lo stesso oggetto (quello a cui puntano entrambi gli a1).
    Okay....mentre se implementassi opportunamente i costruttori di copia in questo modo:

    codice:
    ObjectV::ObjectV(const ObjectV& objV ) 
    {
        std::cout << "Costruttore copia V " << std::endl;
        this->x = objV.x;
        this->a1 = new ObjectA(objV.a1);
    }
    
    
    ObjectA::ObjectA(const ObjectA* objA)
    {
        std::cout << "Costruttore copia A " << std::endl;
        this->y = objA->y;
    }
    dovrei risolvere il problema, infatti se nel main stampo i due membri y dell'oggetto ObjectA ci sono valori differenti.
    "Non può piovere per sempre" Il Corvo
    Forza Vigor!

  8. #8
    Non solo, devi implementare anche l'operatore di assegnazione, altrimenti questo:
    codice:
     
    int main()
    {
        ObjectV v1;
        ObjectV v2;
        v2=v1;
        return 0;
    }
    genera un memory leak e una double free.

    Di nuovo: in genere è molto meglio avere solo membri allocati dentro la classe stessa piuttosto che roba allocata nell'heap, oppure aggirare il problema disabilitando costruttore di copia e operatore di assegnazione.
    Ultima modifica di MItaly; 21-05-2014 a 18:49
    Amaro C++, il gusto pieno dell'undefined behavior.

  9. #9
    Utente di HTML.it L'avatar di MrX87
    Registrato dal
    Jun 2007
    Messaggi
    500
    Quote Originariamente inviata da MItaly Visualizza il messaggio
    Non solo, devi implementare anche l'operatore di assegnazione, altrimenti questo:
    codice:
     
    int main()
    {
        ObjectV v1;
        ObjectV v2;
        v2=v1;
        return 0;
    }
    genera un memory leak e una double free.

    Di nuovo: in genere è molto meglio avere solo membri allocati dentro la classe stessa piuttosto che roba allocata nell'heap, oppure aggirare il problema disabilitando costruttore di copia e operatore di assegnazione.
    Okay vero, mi era sfuggito. Comunque seguirò il consiglio di avere solo membri statici dentro la classe, ma era solo per capire meglio il funzionamento delle cose e avere più chiaro il comportamento del codice che scrivo.

    Grazie ancora delle spiegazioni
    "Non può piovere per sempre" Il Corvo
    Forza Vigor!

  10. #10
    Quote Originariamente inviata da MrX87 Visualizza il messaggio
    Okay vero, mi era sfuggito. Comunque seguirò il consiglio di avere solo membri statici dentro la classe,
    Occhio che i campi static sono un'altra cosa...
    ma era solo per capire meglio il funzionamento delle cose e avere più chiaro il comportamento del codice che scrivo.
    Consiglio: ci sono dei casi in cui è necessario tenere dei puntatori nella classe, o comunque dover gestire delle risorse; in questi casi:
    1. se esistono classi che già fanno quello che vuoi fare incapsulando la gestione della memoria, usa quelle; ovvero, non usare vettori allocati con la new, ma std::vector;
    2. se invece hai puntatori ad oggetti di cui la classe è proprietaria, valuta l'uso di smart pointer, ed eventualmente disabilita copia e assegnazione (dichiarandoli come private e non definendoli, cosa che comunque viene da sé se hai come membri degli oggetti non copiabili) se non è necessaria;
    3. se devi gestire risorse (connessioni a DB, handle a risorse, ...), crea una classe wrapper che si occupi solo di quello (e.g. una classe DBConnection), non fare gestire più risorse direttamente alla stessa classe;
    4. nella classe che gestisce direttamente le risorse, se ha senso ed è ben definita una semantica di copia, definisci sempre assieme i "big three" (costruttore di copia, operatore di assegnamento, distruttore); in tal caso, usa il copy and swap idiom invece di inventarti cose strane, è uno dei pochi metodi che sicuramente funziona per implementarli.
    Amaro C++, il gusto pieno dell'undefined behavior.

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2024 vBulletin Solutions, Inc. All rights reserved.