web-dev-qa-db-fra.com

Utiliser une classe abstraite en C++

J'essaie d'utiliser une classe abstraite lors du passage d'un objet étendu en tant que paramètre à une fonction, mais mes tentatives jusqu'à présent ont entraîné des erreurs de compilation.

J'ai quelques indices sur le problème, je ne suis évidemment pas autorisé à instancier une classe abstraite, et je pense que certains codes de MyClass essaient de le faire, même si ce n'est pas mon intention. Certaines recherches ont suggéré que je devrais faire référence à l'objet en tant que pointeur pour obtenir ce que je veux, mais mes tentatives jusqu'à présent ont échoué et je ne suis même pas sûr que ce soit la réponse (d'où ma demande ici).

Je soumets maintenant que je suis plus familier avec Java que le C++, et je suis sûr qu’une partie de mon problème tient à cela.

Voici un exemple de ce que j'essaie de faire dans mon programme:

class A {
    public:
        virtual void action() = 0;
};

class B : public A {
    public:
        B() {}

        void action() {
            // Do stuff
        }
};

class MyClass {

    public:

        void setInstance(A newInstance) {
            instance = newInstance;
        }

        void doSomething() {
            instance.action();
        }

    private:

        A instance;
};

int main(int argc, char** argv) {
    MyClass c;
    B myInstance;
    c.setInstance(myInstance);
    c.doSomething();
    return 0;
}

Cet exemple produit la même erreur de compilation que celle que je reçois dans mon programme:

sean@SEAN-PC:~/Desktop$ gcc -o test test.cpp
test.cpp:20: error: cannot declare parameter ‘newInstance’ to be of abstract type ‘A’
test.cpp:2: note:   because the following virtual functions are pure within ‘A’:
test.cpp:4: note:   virtual void A::action()
test.cpp:30: error: cannot declare field ‘MyClass::instance’ to be of abstract type ‘A’
test.cpp:2: note:   since type ‘A’ has pure virtual functions
test.cpp: In function ‘int main(int, char**)’:
test.cpp:36: error: cannot allocate an object of abstract type ‘A’
test.cpp:2: note:   since type ‘A’ has pure virtual functions

Mettre à jour

Merci à tous pour vos commentaires.

J'ai depuis changé "MyClass :: instance pour contenir un pointeur de type A, mais je reçois maintenant quelques erreurs bizarres liées à vtable:

sean@SEAN-PC:~/Desktop$ gcc -o test test.cpp
/tmp/ccoEdRxq.o:(.rodata._ZTI1B[typeinfo for B]+0x0): undefined reference to `vtable for __cxxabiv1::__si_class_type_info'
/tmp/ccoEdRxq.o:(.rodata._ZTI1A[typeinfo for A]+0x0): undefined reference to `vtable for __cxxabiv1::__class_type_info'
/tmp/ccoEdRxq.o:(.rodata._ZTV1A[vtable for A]+0x8): undefined reference to `__cxa_pure_virtual'
collect2: ld returned 1 exit status

Mon code modifié est le suivant (A et B n'ont pas été modifiés):

class MyClass {

    public:

        void setInstance(A* newInstance) {
            instance = newInstance;
        }

        void doSomething() {
            instance->action();
        }

    private:

        A* instance;
};

int main(int argc, char** argv) {
    MyClass c;
    B myInstance;
    c.setInstance(&myInstance);
    c.doSomething();
    return 0;
}
16
seanhodges

Votre problème est que vous devez accepter une référence dans votre fonction. La raison en est qu'une référence ne copie pas l'argument passé. Si vous acceptez cependant une A - au lieu d'une référence A& -, vous copiez en fait l'argument transmis dans l'objet paramètre, et vous obtenez un objet de type A - mais ce n'est en fait pas autorisé!

    // the reference parameter will reference the actual argument
    void setInstance(A &newInstance) {
            // assign the address of the argument to the pointer member
            // instance. 
            instance = &newInstance;
    }

Et vous devrez ensuite changer le membre de votre classe pour qu'il soit un pointeur. Ce ne peut pas être une référence car setInstance changera ce qu’elle référence - une référence ne peut référencer qu’un objet pendant toute sa durée de vie, alors qu’un pointeur peut être positionné pour pointer faire différentes choses simplement en lui réaffectant une adresse différente. Les parties restantes ressemblent à ceci alors

    void doSomething() {
        // call a member function on the object pointed to
        // by instance!
        instance->action();
    }

private:

    // a pointer to some object derived from A
    A *instance;

Notez également que vous devez compiler les programmes C++ à l'aide de g++, car il lie en outre la bibliothèque standard C++ à votre code.

g++ -o test test.cpp # instead of gcc!
27

Ce que vous faites fonctionnerait en Java, car déclarer un paramètre ou une variable membre de type "A" signifie en réalité un "pointeur sur un A". En C++, vous devez en réalité être explicite à ce sujet, car ce sont deux choses différentes:

void setInstance(A* newInstance) { // pointer to an "A"
                instance = newInstance;
}

Et dans la déclaration:

A* instance; // Not an actual "A", but a pointer to an "A"
5
Eric Petroelje

Je crois que c'est ce que vous essayez de faire. Cela démontre le polymorphisme en imprimant quelque chose en fonction de si la classe de descripteurs pointe vers une instance de B ou C. D'autres personnes ont bien raison de vouloir également un destructeur virtuel.

Cela compile avec: g ++ test.cpp -o Test 

#include <stdio.h>

class A {
    public:
        virtual void action() = 0;
};

class B : public A {
    public:
        B() {}

        void action() {
                printf("Hello World\n");
        }
};

class C : public A {
    public:
        C() {}

        void action() {
                printf("Goodbye World\n");
        }
};

class AHandleClass {

    public:

        void setInstance(A *A_Instance) {
                APointer = A_Instance;
        }

        void doSomething() {
                APointer->action();
        }

    private:

        A *APointer;
};

int main(int argc, char** argv) {
    AHandleClass AHandle;
    B BInstance;
    C CInstance;
    AHandle.setInstance(&BInstance);
    AHandle.doSomething();
    AHandle.setInstance(&CInstance);
    AHandle.doSomething();
    return 0;
}
3
Robert S. Barnes

Votre problème est maintenant un lien. Pour les programmes C++, la bibliothèque C++ standard doit être ajoutée:

gcc -o test -lstdc ++ test.cpp

3
Dmitry Khalatov

Vous n'êtes pas obligé d'utiliser des pointeurs si vous démissionnez du setter et utilisez un constructeur. C’est l’une des fonctionnalités importantes de C++: les initialiseurs de base dans les constructeurs permettent souvent d’éviter d’utiliser des pointeurs.

class MyClass {

        public:

                MyClass(A & newInstance) : instance(newInstance) {
                }

                void doSomething() {
                        instance.action();
                }

        private:

                A & instance;
};



int main(int argc, char** argv) {
        B myInstance;
        MyClass c(myInstance);
1
anon

Vous devriez stocker A comme un pointeur.

A* instance;

Edit: j'ai déjà écrit "référence". Il y a une différence en C++.

1
stepancheg

J'ai eu ce problème en incluant parent.h avant iostream:

faux:

include "parent.h"
include <iostream>

droite:

include <iostream>
include "parent.h"
1
Eddy

Johannes Schaub - litb est correct.

En C++, la classe abstraite ne peut pas être utilisée comme paramètre ou type de fonction. Nous ne pouvons pas instancier un objet abstrait.

Donc besoin d'utiliser & ou *.

1
ijab

Dmitry est correct, vous devriez utiliser -lstdc ++ si vous utilisez gcc, mais il vaut mieux utiliser g++ à la place. (Même syntaxe).
De plus, vous remarquerez (si vous ajoutez -Wall) que vous obtenez un avertissement indiquant que votre classe avec des fonctions virtuelles est sans destructeur. Il est donc judicieux d’ajouter un destructeur (virtuel) à A comme suit: bien.

0
E Dominique

Vous devez utiliser un pointeur sur A en tant que membre de MyClass.

class MyClass {

    public:

        void setInstance(A *newInstance) {
                instance = newInstance;
        }

        void doSomething() {
                instance->action();
        }

    private:

        A *instance;
};

si vous ne le faites pas, le constructeur MyClass essaiera d’instancier un objet A (comme c’est le cas pour tout objet membre), ce qui n’est pas possible car A est abstrait. 

0
Aiua

Quand tu dis

A instance;

vous allez créer un nouvel objet de type A. Mais vous avez déjà dit que A est une classe abstraite, vous ne pouvez donc pas le faire. Vous devez utiliser un pointeur, comme plusieurs autres l’ont indiqué, ou faire un non-abstrait.

0
anon