Использование абстрактного класса в C ++ - PullRequest
16 голосов
/ 16 марта 2009

Я пытаюсь использовать абстрактный класс при передаче расширенного объекта в качестве параметра функции, но мои попытки до сих пор приводили к некоторым ошибкам компилятора.

У меня есть несколько подсказок о том, в чем заключается проблема, мне явно не разрешено создавать экземпляр абстрактного класса, и я считаю, что часть кода в MyClass пытается это сделать, хотя это и не является моим намерением. Некоторые исследования предлагают, чтобы я достиг ссылки на объект как указатель, чтобы достичь того, чего я хочу, но мои попытки пока не увенчались успехом, и я даже не уверен, что это ответ (отсюда и мой вопрос здесь).

Я сообщу, что теперь я больше знаком с Java, чем с C ++, и уверен, что часть моей проблемы связана с этим.

Вот пример того, что я пытаюсь сделать в своей программе:

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

Этот пример выдает ту же ошибку компилятора, которую я получаю в своей программе:

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

Обновление

Спасибо всем за отзыв.

С тех пор я изменил "MyClass :: instance, чтобы он содержал указатель типа A, но теперь я получаю несколько странных ошибок, связанных с виртуальной таблицей:

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

Мой модифицированный код выглядит следующим образом (A и B не были изменены):

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

Ответы [ 11 ]

27 голосов
/ 16 марта 2009

Ваша проблема в том, что вы должны принять ссылку в вашей функции. Причина в том, что ссылка на самом деле не копирует переданный аргумент. Однако если вы принимаете A - вместо ссылки A& - тогда вы фактически копируете аргумент, переданный в объект параметра, и вы получаете объект типа A - но который на самом деле недопустим!

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

И тогда вам придется изменить член в вашем классе, чтобы он был указателем. Это не может быть ссылка, потому что setInstance изменит то, на что она ссылается - ссылка может ссылаться только на один объект в течение всего времени его жизни, в то время как указатель может быть установлен для указания на разные вещи, просто переназначая ему другой адрес. Остальные части выглядят так:

    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;

Также обратите внимание, что вы должны компилировать программы на C ++, используя g++, потому что это дополнительно связывает стандартную библиотеку C ++ с вашим кодом

g++ -o test test.cpp # instead of gcc!
5 голосов
/ 16 марта 2009

То, что вы делаете, будет работать в Java, потому что объявление параметра или переменной-члена типа «A» действительно означает «указатель на A». В C ++ вам действительно нужно четко об этом говорить, поскольку это две разные вещи:

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

А в декларации:

A* instance; // Not an actual "A", but a pointer to an "A"
3 голосов
/ 16 марта 2009

Ваша проблема сейчас связана. Для программы C ++ необходимо добавить стандартную библиотеку C ++:

gcc -o test -lstdc ++ test.cpp

3 голосов
/ 16 марта 2009

Я верю, что это то, что вы пытаетесь сделать. Он демонстрирует полиморфизм, фактически распечатывая что-либо в зависимости от того, указывает ли класс дескриптора на экземпляр B или C. Другие верны, что вам, вероятно, также понадобится виртуальный деструктор.

Это компилируется с: 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;
}
1 голос
/ 23 ноября 2012

Йоханнес Шауб - Литб правильно.

В C ++ абстрактный класс нельзя использовать в качестве параметра или типа возвращаемого значения. Мы не можем создать экземпляр абстрактного объекта.

Так что нужно использовать & или *.

1 голос
/ 20 декабря 2010

У меня была эта проблема, включая parent.h до iostream:

неправильно:

include "parent.h"
include <iostream>

право:

include <iostream>
include "parent.h"
1 голос
/ 17 марта 2009

Вам не нужно использовать указатели , если вы уходите из сеттера и используете конструктор. Это одна из важных функций в C ++: базовые инициализаторы в конструкторах часто позволяют избегать использования указателей.

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 голос
/ 16 марта 2009

Вы должны хранить как указатель.

A* instance;

Редактировать: я уже писал "ссылку". Есть разница в C ++.

0 голосов
/ 16 марта 2009

Дмитрий прав, вы должны использовать -lstdc ++, если вы используете gcc, но еще лучше использовать g++ вместо этого. (Синтаксис тот же).
Кроме того, вы могли бы заметить (я полагаю, если вы добавите -Wall), что вы получите предупреждение о том, что ваш класс с виртуальными функциями не имеет деструктора, поэтому хорошей идеей является также добавление (виртуального) деструктора к A.

0 голосов
/ 16 марта 2009

Когда вы говорите

A instance;

вы создадите новый объект типа A. Но вы уже сказали, что A является абстрактным классом, поэтому вы не можете этого сделать. Вы должны использовать указатель, как указали несколько других, или сделать A неабстрактным.

...