16 Stimmen

Verwendung einer abstrakten Klasse in C++

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

Ich habe einige Hinweise darauf, was das Problem ist, ich bin offensichtlich nicht erlaubt, eine abstrakte Klasse zu instanziieren, und ich glaube, einige der Code in MyClass versucht, dies zu tun, auch wenn dies nicht meine Absicht ist. In einigen Untersuchungen wurde vorgeschlagen, dass ich das Objekt als Zeiger referenzieren sollte, um das zu erreichen, was ich will, aber meine bisherigen Versuche sind gescheitert, und ich bin nicht einmal sicher, dass dies die Antwort ist (daher meine Frage hier).

Ich gebe zu, 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 dafür, was ich in meinem Programm zu tun 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 denselben Compilerfehler, den ich in meinem Programm erhalte:

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

Update

Vielen Dank für das Feedback.

Ich habe seitdem "MyClass::instance geändert, um einen Zeiger des Typs A enthalten, aber ich bekomme jetzt einige bizarre Fehler im Zusammenhang mit der 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

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

28voto

Johannes Schaub - litb Punkte 479831

Ihr Problem ist, dass Sie einen Verweis in Ihrer Funktion akzeptieren sollten. Der Grund dafür ist, dass eine Referenz das übergebene Argument nicht wirklich kopiert. Wenn Sie jedoch eine A - anstelle einer Referenz A& - kopieren Sie das übergebene Argument in das Parameterobjekt, und Sie erhalten ein Objekt vom Typ A - was aber eigentlich nicht erlaubt 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;
    }

Dann müssen Sie das Element in Ihrer Klasse so ändern, dass es ein Zeiger ist. Es kann keine Referenz sein, weil setInstance ändert, worauf er verweist - ein Verweis kann während seiner gesamten Lebensdauer nur auf ein Objekt verweisen, während ein Zeiger auf verschiedene Dinge verweisen kann, indem ihm einfach eine andere Adresse zugewiesen wird. Die übrigen Teile sehen dann wie folgt 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 auch, dass Sie C++-Programme mit g++ weil es zusätzlich die C++-Standardbibliothek mit Ihrem Code verknüpft

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

0 Stimmen

Hervorragend, mein zweites vtable-Problem war auf die Verwendung von gcc anstelle von g++ zurückzuführen. Danke!

1 Stimmen

Wenn Sie vorhaben, ein 'B'-Objekt über einen 'A'-Zeiger zu löschen, definieren Sie A::~A als virtuell.

5voto

Eric Petroelje Punkte 58501

Was Sie tun, würde in Java funktionieren, weil die Deklaration eines Parameters oder einer Mitgliedsvariablen vom Typ "A" in Wirklichkeit einen "Zeiger auf ein A" bedeutet. In C++ müssen Sie das 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"

0 Stimmen

@n0rd - Mein Fehler, ich war im Begriff, Referenzen zu verwenden und wechselte dann zu Zeigern, nachdem ich bemerkt hatte, dass seine Erklärung nicht als Referenz funktionieren würde (Referenzen können nicht null sein). Ich werde es bearbeiten und korrigieren.

0 Stimmen

Was Sie erklärt, macht Sinn, aber da ich "Instanz" als Zeiger deklariert habe, erhalte ich Linker Fehler im Zusammenhang mit der vtable... Ich werde meine Frage aktualisieren, um dies zu erweitern.

3voto

Robert S. Barnes Punkte 38141

Ich glaube, das ist es, was Sie zu tun versuchen. Es demonstriert den Polymorphismus, indem es tatsächlich etwas ausgibt, je nachdem, ob die Handle-Klasse auf eine Instanz von B oder C zeigt. Andere haben Recht, dass Sie wahrscheinlich auch einen virtuellen Destruktor benötigen.

Dies wird kompiliert mit: 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;
}

0 Stimmen

Vielen Dank für die Veröffentlichung dieser bis, ich versucht, es zu kompilieren und es produziert die gleichen vtable Fehler, die ich derzeit in meinem aktualisierten Code erhalten. Irgendwelche Ideen?

0 Stimmen

Am Ende habe ich versehentlich -> verwendet, wo ich eigentlich . Es kompiliert jetzt und ich habe einige Dinge hinzugefügt, um es informativer zu machen.

0 Stimmen

Das funktioniert gut, weil es auf der Hauptleitung liegt, aber in anderen Situationen, wenn AHandle den Anwendungsbereich übersteigt, in dem sowohl BInstance y CInstance deklariert sind, wird der Verweis ungültig und der Speicher wird beschädigt.

3voto

Dmitry Khalatov Punkte 4153

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

gcc -o test -lstdc++ test.cpp

1 Stimmen

Sie sollten hier wirklich g++ anstelle von gcc verwenden. Es ist mehr als nur -lstdc++.

1voto

stepancheg Punkte 4124

Sie sollten A als Zeiger speichern.

A* instance;

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

0 Stimmen

Das ist kein Verweis, sondern ein Zeiger. Bitte bearbeiten Sie Ihre Antwort.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X