it-swarm.com.de

Verwendung einer abstrakten Klasse in C++

Ich versuche, eine abstrakte Klasse zu verwenden, wenn Sie ein erweitertes Objekt als Parameter an eine Funktion übergeben, aber meine bisherigen Versuche haben zu einigen Compiler-Fehlern geführt.

Ich habe ein paar Anhaltspunkte, was das Problem ist, offensichtlich darf ich keine abstrakte Klasse instanziieren, und ich glaube, ein Teil des Codes in MyClass versucht dies, obwohl dies nicht meine Absicht ist. Einige Untersuchungen haben ergeben, dass ich das Objekt als Zeiger verwenden sollte, um das zu erreichen, was ich will, aber meine bisherigen Versuche sind gescheitert und ich bin mir nicht einmal sicher, ob dies die Antwort ist (daher meine Anfrage hier).

Ich werde jetzt einreichen, dass ich mit Java besser vertraut bin als mit C++, und ich bin sicher, dass ein Teil meines Problems darauf zurückzuführen ist.

Hier ist ein Beispiel für das, was ich in meinem Programm versuche:

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;
}

Dieses Beispiel erzeugt den gleichen Compiler-Fehler, den ich in meinem Programm bekomme:

[email protected]:~/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

Aktualisieren

Vielen Dank für das Feedback.

Ich habe seitdem "MyClass :: instance" geändert, um einen Zeiger vom Typ A zu enthalten, aber jetzt bekomme ich einige bizarre Fehler in Bezug auf die vtable:

[email protected]:~/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

Mein geänderter Code lautet wie folgt (A und B wurden nicht geändert):

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

Ihr Problem ist, dass Sie einen Verweis in Ihrer Funktion akzeptieren sollten. Der Grund ist, dass eine Referenz das übergebene Argument nicht tatsächlich kopiert. Wenn Sie jedoch eine A - anstelle einer Referenz A& - akzeptieren, kopieren Sie tatsächlich das in das Parameterobjekt übergebene Argument, und Sie erhalten ein Objekt vom Typ A -, das jedoch nicht zulässig ist!

    // 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;
    }

Und dann müssen Sie das Mitglied in Ihrer Klasse als Zeiger ändern. Es kann keine Referenz sein, da setInstance die Referenz ändert, auf die sie verweist. Eine Referenz kann während ihrer gesamten Lebensdauer nur auf ein Objekt verweisen, während ein Zeiger so eingestellt werden kann, dass er verschiedene Dinge durch einfaches Zuweisen einer anderen Adresse anzeigt. Die übrigen Teile sehen dann so aus

    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;

Beachten Sie außerdem, dass Sie C++ - Programme mit g++ kompilieren müssen, da die C++ - Standardbibliothek zusätzlich mit Ihrem Code verknüpft wird

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

Was Sie tun, funktioniert in Java, weil die Deklaration eines Parameters oder einer Membervariablen vom Typ "A" wirklich einen "Zeiger auf ein A" bedeutet. In C++ müssen Sie dies explizit angeben, da es sich um zwei verschiedene Dinge handelt:

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

Und in der Erklärung:

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

Ich glaube, das ist es, was Sie versuchen zu tun. Es demonstriert den Polymorphismus, indem es tatsächlich etwas ausgibt, abhängig davon, ob die Klasse des Handles auf eine Instanz von B oder C verweist. Andere sind der Meinung, dass Sie wahrscheinlich auch einen virtuellen Destruktor wünschen.

Dies wird mit g ++ test.cpp -o Test kompiliert 

#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

Ihr Problem ist jetzt eine Verknüpfung. Für ein C++ - Programm muss eine Standard-C++ - Bibliothek hinzugefügt werden:

gcc -o test -lstdc ++ test.cpp

3
Dmitry Khalatov

Sie müssen keine Zeiger verwenden wenn Sie vom Setter zurücktreten und einen Konstruktor verwenden. Dies ist eine der wichtigsten Funktionen in C++: Basisinitialisierer in Konstruktoren erlauben oft die Verwendung von Zeigern.

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

Sie sollten A als Zeiger speichern.

A* instance;

Edit: Ich habe schon "Referenz" geschrieben. Es gibt einen Unterschied in C++.

1
stepancheg

Ich hatte dieses Problem mit parent.h vor iostream:

falsch:

include "parent.h"
include <iostream>

recht:

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

Johannes Schaub - litb ist richtig.

In C++ kann die Abstract-Klasse nicht als Parameter oder Rückgabetyp der Funktionen verwendet werden. Wir können ein abstraktes Objekt nicht instanziieren.

Verwenden Sie also & oder *.

1
ijab

Dmitry ist richtig, Sie sollten -lstdc ++ verwenden, wenn Sie gcc verwenden, aber noch besser ist g++. (Gleiche Syntax).
Außerdem würden Sie beachten (ich schätze, wenn Sie -Wall hinzufügen), dass Sie eine Warnung erhalten, dass Ihre Klasse mit virtuellen Funktionen ohne Destruktor ist. Daher ist es eine gute Idee, A einen (virtuellen) Destruktor hinzuzufügen Gut.

0
E Dominique

Sie müssen einen Zeiger auf A als Mitglied von MyClass verwenden.

class MyClass {

    public:

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

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

    private:

        A *instance;
};

wenn Sie dies nicht tun, versucht MyClass-Konstruktor, ein A-Objekt zu instantiieren (wie bei jedem Member-Objekt). Dies ist nicht möglich, da A abstrakt ist. 

0
Aiua

Wenn du sagst

A instance;

sie werden ein neues Objekt vom Typ A erstellen. Aber Sie haben bereits gesagt, dass A eine abstrakte Klasse ist, also können Sie das nicht. Sie müssen einen Zeiger verwenden, wie mehrere andere angegeben haben, oder A nicht abstrakt machen.

0
anon