IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 C++ Discussion :

Tableau d'objets de classe abstraites?


Sujet :

C++

  1. #1
    Membre très actif
    Homme Profil pro
    Inscrit en
    Janvier 2011
    Messages
    247
    Détails du profil
    Informations personnelles :
    Sexe : Homme

    Informations forums :
    Inscription : Janvier 2011
    Messages : 247
    Par défaut Tableau d'objets de classe abstraites?
    Salut à tous!
    J'obtiens l'erreur cannot allocate an object of abstract type... dans mon programme.
    Est-ce que j'ai le droit de créer des pointeurs sur classes abstraites?
    Voici une partie de mon code:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
     
    //Le main
    int main()
    {
    	Employe* pauvres;
    	//Cinq* riches[3];
     
    	pauvres=new Employe;  //là je fais un test et j'obtiens l'erreur.
    /*	for(int i=0;i<3;i++)
    	{
    		pauvres[i]=new Trente("toto",2,3);
    	}
     
    	*pauvres[0]=Trente("Dupont",28,2500);
    	*pauvres[1]=Trente("Tintin",54,2000);
    	*pauvres[2]=Trente("Milou",35,2900);
    	*riches[0]=Cinq("Picsou",88,3000);
    	*riches[1]=Cinq("Flairsou",77,2300);
    	*riches[2]=Cinq("Gripsou",66,3100);
     
    	for(int i=0;i<1;i++)
    	{
     
    //		pauvres[i]->getNom();
    //		cout<<"Le pauvre "<<pauvres[i]->getNom()<<" gagne "<<pauvres[i]->getSalaire()<<" ( "<<pauvres[i]->getTarif()<<" /heure"<<endl;
    //		cout<<"Le riche "<<riches[i]->getNom()<<" gagne "<<riches[i]->getSalaire()<<" ( "<<riches[i]->getTarif()<<" /heure"<<endl;
    	}*/
    }
     
    //Un header
    class Employe{
    	protected:
    		string nom;
    		int heure;
    		int tarif;
    	public:
    		Employe();
    		Employe(string n, int h,int t);
    		Employe(Employe &e);
    		virtual int getSalaire()=0;
    		virtual void setInfoSalaire(int t,int h)=0;
    		string getNom();
    		int getTarif();
    };
    Quelqu'un pourrait-il m'aider svp?

  2. #2
    Membre Expert
    Homme Profil pro
    Inscrit en
    Décembre 2011
    Messages
    1 186
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Décembre 2011
    Messages : 1 186
    Par défaut
    Salut,

    La classe Employe est abstraite.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    class Employe
    {
    //...
       virtual int getSalaire()=0;
       virtual void setInfoSalaire(int t,int h)=0;
    //..
    }
    Est-ce que j'ai le droit de créer des pointeurs sur classes abstraites?
    Il n'est pas possible d'instancier d'objet d'un type abstrait. Le pointeur peut être du type Employe, mais pas l'objet.

    Il faut implémenter les fonctions virtuelles pures soit dans cette classe, soit dans une classe dérivée.

    voir FAQ classe abstraite

  3. #3
    Membre très actif
    Homme Profil pro
    Inscrit en
    Janvier 2011
    Messages
    247
    Détails du profil
    Informations personnelles :
    Sexe : Homme

    Informations forums :
    Inscription : Janvier 2011
    Messages : 247
    Par défaut
    Citation Envoyé par BlueMonkey Voir le message
    Salut,

    Le pointeur peut être du type Employe, mais pas l'objet.
    C'est-à-dire?

  4. #4
    Membre éprouvé Avatar de Xtrem_Voyageur
    Homme Profil pro
    Inscrit en
    Juin 2009
    Messages
    85
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Secteur : Finance

    Informations forums :
    Inscription : Juin 2009
    Messages : 85
    Par défaut
    Quand on décide de faire d'une classe, une classe abstraite, c'est qu'on ne souhaite pas pouvoir instancier cette classe, c'est-à-dire avoir des objets du type de cette classe.

    Par exemple, on cherche souvent à représenter des concepts réels sous forme de classe, souvent il y a des concepts qui restent trop globaux et n'ont pas de sens réel physique. On donne souvent l'exemple des Formes :
    un carré, un losange, un triangle et un cercles sont tous des formes. Pourtant on ne souhaitera pas manipuler juste un objet du type Forme, qui n'a pas de réel sens, une forme sera un carré, un losange, etc, un hexagone, ... Du coup, Forme sera une classe abstraite.

    Pour qu'une classe soit abstraite en C++, il suffit qu'elle ait une méthode virutelle pure qui devra être (re)définie dans une des classes dérivées.
    Exemple : ma classe Forme suivante est abstraite

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
     
    class Forme
    {
    public:
    virtual void dessine() = 0; // Ceci est une méthode virtuelle pure (virtuelle = virtual / pure car "=0")
    };
     
    class Triangle : public Forme
    {
    public:
    void dessine() { ... } // Ici tu définis et donnes un corps à dessine(), Si Triangle n'a pas d'autres membres virtuels purs, alors tu peux l'instancier.
    };
     
    int main()
    {
       Forme f = Forme() // Ici tu obtiens une erreur car tu ne peux pas instancier un objet de type Forme car c'est une classe abstraite.
     
       Triangle t = Triangle(); // Ok
     
       Forme *f = new Triangle() // Ok, tu peux aussi avoir un pointeur sur un type abstrait, la classe de base de ta hiérarchie de classe.
     
       return 0;
    }

  5. #5
    Membre très actif
    Homme Profil pro
    Inscrit en
    Janvier 2011
    Messages
    247
    Détails du profil
    Informations personnelles :
    Sexe : Homme

    Informations forums :
    Inscription : Janvier 2011
    Messages : 247
    Par défaut
    Je m'inspire de ton code, mais j'obtiens des erreurs, toujours au niveau du main().
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
     
    //ma classe mère
    class Employe{
    	protected:
    		string nom;
    		int heure;
    		int tarif;
    	public:
    		Employe();
    		Employe(string n, int h,int t);
    		Employe(Employe &e);
    		virtual int getSalaire()=0;
    		virtual void setInfoSalaire(int t,int h)=0;
    		string getNom();
    		int getTarif();
    };
    //une classe fille
    class Trente:public Employe
    {
    	public:
    		Trente();
    		Trente(string n, int h, int t);
    		Trente(Trente& t);
    		int getSalaire();
    		void setInfoSalaire(int t, int h);
    };
     
    int main()
    {
    	Trente pauvres= Trente(); //Là j'obtiens l'erreur
    }
    L'erreur est la suivante: no matching function for call to ‘Trente::Trente(Trente)’
    ...

  6. #6
    Membre éprouvé Avatar de Xtrem_Voyageur
    Homme Profil pro
    Inscrit en
    Juin 2009
    Messages
    85
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Secteur : Finance

    Informations forums :
    Inscription : Juin 2009
    Messages : 85
    Par défaut
    Avant de s'inspirer, il vaut mieux comprendre. Que cherches-tu vraiment à faire?
    Quelle sera ta hiérarchie de classe finale?

    Ensuite dans ton code, où sont les corps des membres?

    Dans ta classe Trente, tu as 3 constructeurs, les 2 premiers peuvent être facilement regroupés en un seul en utilisant des arguments par défaut.
    Quant au 3ème, le constructeur de copie, pourquoi le déclares-tu, celui généré par défaut ne te suffit pas?

    Ensuite dans ta classe de base, il serait bien de qualifier le destructeur de virtuel.

    Avant d'entrer et d'explorer tous ces détails, qu'aimerais tu faire?

    Car il y a beaucoup de choses à dire sur ton code donc mieux vaut orienter la discussion suivant ce que tu souhaites faire.

  7. #7
    Membre très actif
    Homme Profil pro
    Inscrit en
    Janvier 2011
    Messages
    247
    Détails du profil
    Informations personnelles :
    Sexe : Homme

    Informations forums :
    Inscription : Janvier 2011
    Messages : 247
    Par défaut
    -Le corps des membres sont dans d'autres fichiers, je les joins à la fin de ce post.
    -A l'origine j'avais utilisé un constructeur de copie avec des valeurs par défaut, mais je l'ai divisé en deux fonctions parce que je croyais que l'erreur venait de là.
    -Le dernier constructeur est là au cas où.

    Alors voici le corps des fonctions:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
     
    //Les constructeurs de la sous-classe Trente
    Trente::Trente():Employe(){
    }
    Trente::Trente(string n, int h, int t):Employe(n,h,t){
    }
     
    Trente::Trente(Trente& t):Employe(t){
    }
    //Les constructeurs de la classe mère Employe
    Employe::Employe()
    {
    	nom="";
    	heure=0;
    	tarif=0;
    }
    Employe::Employe(string n, int h,int t)
    {
    	nom=n;
    	heure=h;
    	tarif=t;
    }
    Employe::Employe(Employe& e)
    {
    	nom=e.nom;
    	heure=e.heure;
    	tarif=e.tarif;
    }

  8. #8
    Membre éprouvé Avatar de Xtrem_Voyageur
    Homme Profil pro
    Inscrit en
    Juin 2009
    Messages
    85
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Secteur : Finance

    Informations forums :
    Inscription : Juin 2009
    Messages : 85
    Par défaut
    Bon Parlons tout d'abord du constructeur de copie. Un prototype plus correct serait
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    Employe(const Employe& e); 
    Trente(const Trente& t);
    Comme son nom l'indique le constructeur de copie sert à copier des objets, c'est lui qui sera invoqué notamment quand tu passeras des objets de type Employe ou Trente 'par valeur' en arguments de fonctions ou que tu renverras 'par valeur' des objets de type Employe ou Trente
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    void doSomething(Employee arg) { ... } 
    /* Quelque part dans ton code*/
    Employe e("Bob", 9, 50);
    doSomething(e); // Passage par valeur : constructeur de copie appelé pour copier e dans arg, cela permet à la fonction doSomething de travailler avec une copie de e et ne pas modifier e, à la fin de la fonction arg est détruit.
     
    Trente doSomething() { Trente res = Trente(); ... return res;}
    /* Quelque part dans ton code */
    Trente t = doSomething(); // Constructeur de copie appelé pour copier res dans t
    Il est aussi utilisé pour copier 'directement' des objets (un peu comme du clônage si tu veux)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    Employe e1("Bruno", 12, 150);
    Employe clone_de_e1(e1);  // Ici le constructeur d'Employe est utilisé
    A savoir : si tu ne le déclares pas, il est généré par défaut par le compilateur, donc si tu n'as pas de bonnes raisons pour le redéfinir, ne le déclares pas.
    Tu pourrais aussi avoir des raisons d'interdire la copie, un employe doit-il pouvoir être clôné? Pour cela tu pourrais le déclarer dans la partie private.


    Ensuite ne fais qu'un seul constructeur :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    Employe(const string& s  = "John Doe", int h = 8, int t = 100);
    Employe(const string& s = "", int h = 0, int t = 0); // si tu veux que les valeurs par défaut soit la chaîne vide et 0 puis 0.

  9. #9
    Membre très actif
    Homme Profil pro
    Inscrit en
    Janvier 2011
    Messages
    247
    Détails du profil
    Informations personnelles :
    Sexe : Homme

    Informations forums :
    Inscription : Janvier 2011
    Messages : 247
    Par défaut
    Merci pour les précisions.
    Cependant mes erreurs proviennent toutes des fichiers tmp lors de la compilation.
    Elles sont toutes de la forme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    trente.cpp:(.text+0x4c): undefined reference to `std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)'
    Tu n'as pas une idée d'où elles peuvent provenir?

  10. #10
    Membre éprouvé Avatar de Xtrem_Voyageur
    Homme Profil pro
    Inscrit en
    Juin 2009
    Messages
    85
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Secteur : Finance

    Informations forums :
    Inscription : Juin 2009
    Messages : 85
    Par défaut
    Ensuite tu as mis dans ton code :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    //Les constructeurs de la classe mère Employe
    Employe::Employe()
    {
    	nom="";
    	heure=0;
    	tarif=0;
    }
    Employe::Employe(string n, int h,int t)
    {
    	nom=n;
    	heure=h;
    	tarif=t;
    }
    On a pour habitude d'utiliser une liste d'initialisation, c'est plus efficace et évite de faire une opération d'assignation inutile.
    Exemple, on fait une classe très simple :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    class A
    {
    public:
       A(const std::string& arg);
    private:
       std::string nom;
    };
    Définissions le constructeur de 2 façons :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    // La première façon
    A::A(const std::string& arg)
    {
       nom = arg;
    } 
     
    // La deuxième façon
    A::A(const std::string& arg) : nom(arg) {}
    Dans la première façon (celle que tu utilises), saches qu'avant d'entrer dans le corps du constructeur délimité par les accolades {}, un appel au constructeur par défaut de std::string est fait pour le membre 'nom', donc avant d'entrer dans le corps on se retrouve avec nom qui est une chaîne vide. Ensuite on entre dans le corps et on exécute l'instruction 'nom=arg' où l'assignation est réalisée.

    Dans la seconde façon de faire, std::string fait appel à son constructeur de copie pour copier arg dans nom... on a donc plus besoin de faire d'assignation et le corps du constructeur est donc vide..

    A toi de modifier correctement tes constructeurs pour utilisé les arguments par défaut et les listes d'initialisation.

  11. #11
    Membre éprouvé Avatar de Xtrem_Voyageur
    Homme Profil pro
    Inscrit en
    Juin 2009
    Messages
    85
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Secteur : Finance

    Informations forums :
    Inscription : Juin 2009
    Messages : 85
    Par défaut
    Repostes tout ton code en intégralité lorsque tu auras fait ces quelques modifications les constructeurs (2 en 1) et enlever les constructeurs de copie inutiles ici..
    Ensuite nous verrons tes erreurs, d'ailleurs celle-ci est assez explicite.

  12. #12
    Membre très actif
    Homme Profil pro
    Inscrit en
    Janvier 2011
    Messages
    247
    Détails du profil
    Informations personnelles :
    Sexe : Homme

    Informations forums :
    Inscription : Janvier 2011
    Messages : 247
    Par défaut
    Le voici le voila:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
     
    class Employe{
    	protected:
    		string nom;
    		int heure;
    		int tarif;
    	public:
    		Employe(string n="", int h=0,int t=0);
    //		Employe(Employe &e);
    		virtual int getSalaire()=0;
    		virtual void setInfoSalaire(int t,int h)=0;
    		string getNom();
    		int getTarif();
    };
    class Trente:public Employe
    {
    	public:
    		Trente(string n="", int h=0, int t=0);
    //		Trente(Trente& t);
    		int getSalaire();
    		void setInfoSalaire(int t, int h);
    };
     
    Employe::Employe(string n, int h,int t)
    {
    	nom=n;
    	heure=h;
    	tarif=t;
    }
    Trente::Trente(string n, int h, int t):Employe(n,h,t){
    }
    J'utilise le namespace std à chaque fois, et je n'ai posté que les définitions et les constructeurs parce que le problème ne peut venir que de là.
    PS: J'adore le fait d'utiliser le constructeur de copie de string.

  13. #13
    Membre éprouvé Avatar de Xtrem_Voyageur
    Homme Profil pro
    Inscrit en
    Juin 2009
    Messages
    85
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Secteur : Finance

    Informations forums :
    Inscription : Juin 2009
    Messages : 85
    Par défaut
    Si tu changes le prototype de la déclaration d'une fonction dans ta classe, il faut que ce prototype soit le même lorsque tu définis le corps de ta fonction à l'extérieur de ta classe!

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    class Employe{
    	protected:
    		string nom;
    		int heure;
    		int tarif;
    	public:
    		Employe(const string& n="", int h=0,int t=0);
    		Employe(const Employe &e); // Ca je ne veux plus le voir! Enleves cette ligne, elle ne te sert pas
                    virtual ~Employe();
    		virtual int getSalaire() const =0;
    		virtual void setInfoSalaire(int t,int h) =0;
    		string getNom() const;
    		int getTarif() const;
    };
    
    class Trente:public Employe
    {
    	public:
    		Trente(const string& n="", int h=0, int t=0);
    		Trente(const Trente& t);
    		int getSalaire() const;
    		void setInfoSalaire(int t, int h) const;
    };
     
    Employe::Employe(const string& n="", int h=0,int t=0) : nom(n), heure(h), tarif(t) {}
    
    Trente::Trente(const string& n="", int h=0, int t=0) : Employe(n,h,t) {}
    
    
    Employe::~Employe() {}
    

  14. #14
    Membre très actif
    Homme Profil pro
    Inscrit en
    Janvier 2011
    Messages
    247
    Détails du profil
    Informations personnelles :
    Sexe : Homme

    Informations forums :
    Inscription : Janvier 2011
    Messages : 247
    Par défaut
    J'ai trouvé la solution et c'est l'erreur la plus idiote que j'aie faite à ce jour!
    Depuis le début mon code était "correct", bien qu'il pourrait être amélioré.
    Le problème est au niveau du compilateur: j'ai oublié d'utiliser G++, j'ai pris GCC à la place.
    Merci quand même pour les précisions

  15. #15
    Membre éprouvé Avatar de Xtrem_Voyageur
    Homme Profil pro
    Inscrit en
    Juin 2009
    Messages
    85
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Secteur : Finance

    Informations forums :
    Inscription : Juin 2009
    Messages : 85
    Par défaut
    Ah oui, moi je te proposais de continuer à améliorer ta façon de coder avant de s'attaquer à l'erreur. Tu devrais vraiment chercher à mieux comprendre ce que tu fais car en lisant ton code, on se rend tout de suite compte que tu as de très mauvaises bases. Si tu t'es lancé dans la programmation sans jamais ouvrir un livre ou avoir lu un bon cours, tu n'avanceras pas bien loin.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Tableau et classe abstraite
    Par NFHnv dans le forum Collection et Stream
    Réponses: 2
    Dernier message: 18/03/2009, 15h28
  2. Réponses: 7
    Dernier message: 22/10/2008, 10h14
  3. le .class d'un tableau d'objet dynamique
    Par Invité dans le forum Collection et Stream
    Réponses: 10
    Dernier message: 11/07/2008, 10h29
  4. Tableau et classes abstraites
    Par HNoury dans le forum Collection et Stream
    Réponses: 13
    Dernier message: 02/06/2007, 19h49
  5. [POO] Tableau d'objets B dans une instance de class A
    Par Invité dans le forum Langage
    Réponses: 4
    Dernier message: 24/05/2006, 09h52

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo