Использование абстрактного класса в С++
Я пытаюсь использовать абстрактный класс при передаче расширенного объекта в качестве параметра функции, но мои попытки до сих пор приводили к некоторым ошибкам компилятора.
У меня есть несколько подсказок относительно того, в чем проблема, я, очевидно, не могу создавать экземпляр абстрактного класса, и я считаю, что часть кода в 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;
}
Ответы
Ответ 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.