Подтвердить что ты не робот

Использование абстрактного класса в С++

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

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

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

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

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

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

[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

Update

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

С тех пор я изменил экземпляр MyClass::, чтобы содержать указатель типа A, но теперь я получаю некоторые странные ошибки, связанные с 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

Модифицированный код выглядит следующим образом (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;
}
4b9b3361

Ответ 1

Ваша проблема в том, что вы должны принять ссылку в своей функции. Причина в том, что ссылка фактически не копирует переданный аргумент. Если вы, однако, принимаете 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;

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

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

Ответ 2

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

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

И в объявлении:

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

Ответ 3

Я считаю, что это то, что вы пытаетесь сделать. Он демонстрирует полиморфизм, фактически распечатывая что-то в зависимости от того, указывает ли класс handle на экземпляр 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;
}

Ответ 4

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

gcc -o test -lstdС++ test.cpp

Ответ 5

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

A* instance;

Изменить: я написал "ссылку" раньше. В С++ есть разница.

Ответ 6

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

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

Ответ 7

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

не так:

include "parent.h"
include <iostream>

право:

include <iostream>
include "parent.h"

Ответ 8

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

В С++ абстрактный класс не может использоваться как функция param или возвращаемый тип. Мы не можем создать абстрактный объект.

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

Ответ 9

Вы должны использовать указатель на A в качестве члена MyClass.

class MyClass {

    public:

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

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

    private:

        A *instance;
};

Если вы этого не сделаете, конструктор MyClass попытается создать экземпляр объекта A (как и для любого объекта-члена), что невозможно, так как A является абстрактным.

Ответ 10

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

A instance;

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

Ответ 11

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