Я создал интерфейсы (абстрактные классы), которые расходуют другие интерфейсы в C++, и я попытался их реализовать, но при компиляции возникают ошибки.
Вот ошибки:
main.cpp: In function 'int main()':
main.cpp:36:38: error: cannot allocate an object of abstract type 'Subclass'
Subclass * subObj = new Subclass();
^
Subclass.h:13:7: note: because the following virtual functions are pure within 'Subclass':
class Subclass : SubInterface {
^
SuperInterface.h:13:18: note: virtual void SuperInterface::doSomething()
virtual void doSomething()=0;
Вот мои источники:
#include <iostream>
class SuperInterface {
public:
virtual void doSomething() = 0;
protected:
int someValue;
};
class SubInterface : public SuperInterface {
public:
virtual void doSomethingElseThatHasNothingToDoWithTheOtherMethod() = 0;
protected:
int anotherValue;
};
class Superclass : public SuperInterface {
public:
Superclass() {}
virtual ~Superclass() {}
void doSomething() {std::cout << "hello stackoverflow!";}
};
class Subclass : public SubInterface {
public:
Subclass() {}
virtual ~Subclass() {}
void doSomethingElseThatHasNothingToDoWithTheOtherMethod() {std::cout << "goodbye stackoverflow!";}
};
int main(void)
{
Superclass * superObj = new Superclass();
Subclass * subObj = new Subclass();
}
Вот то, что я хочу: я хочу, чтобы моя реализация была в курсе и поэтому имела такое же поведение, как и уже переопределенные методы (например, subObj->doSomething()
работает без необходимости его повторного использования). Может ли кто-нибудь сказать мне, что я должен сделать, чтобы это произошло, если это возможно? Благодарю.
Нет, вы не можете делать то, что хотите, через простое наследование. Ни в doSomething()
не наследует или не обеспечивает реализацию doSomething()
, поэтому вы не можете вызвать subObj->doSomething()
по вашему желанию. Вы должны соблюдать контракт интерфейса subInterface
.
Вы можете наследовать Subclass
из Superclass
и Subinterface
и просто реализовать doSomething()
как своего рода прокси-сервер, Superclass::doSomething()
. Вы все еще нуждаетесь в реализации, но вам не нужно "повторно реализовывать" ее.
Вы получаете ошибку, потому что пытаетесь создать объект абстрактного класса. Ваш Subclass
является абстрактным классом из-за этой строки void doSomethingElse()=0;
, Если класс имеет одну чистую виртуальную функцию, это будет абстрактный класс. Вы не можете создать объект абстрактного класса, у вас может быть только ссылка или указатель на него.
Чтобы избавиться от ошибки, объявление doSomethingElse
в Subclass
должно быть
void doSomethingElse();
Вместо void doSomethingElse()=0;
Также я не понимаю, почему вам нужны два интерфейса. Вы можете получить Subclass
из SuperInterface
, поскольку он в основном такой же, как SubInterface
Ваш класс "Подкласс" должен переопределить 2 чистых виртуальных метода, поэтому:
class Subclass : SubInterface {
public:
Subclass();
virtual ~Subclass();
void doSomethingElse() override;
void doSomething() override;
};
не делая этого или заявляя
void doSomethingElse() = 0;
класс Подкласс также становится абстрактным, который не может быть создан. Вы могли бы взглянуть на: http://www.parashift.com/c++-faq-lite/pure-virtual-fns.html
Вот то, что я хочу: я хочу, чтобы моя реализация была в курсе и поэтому имела такое же поведение, что и для уже переопределенных методов (например, subObj-> метод doSomething() работает без необходимости его повторного использования). Может ли кто-нибудь сказать мне, что я должен сделать, чтобы это произошло, если это возможно??? Благодарю.
→ возможно объявить методы виртуальными не чистыми виртуальными
Честно говоря, я не совсем уверен, что ваш дизайн хочет выразить, но здесь есть как минимум две технические ошибки:
1.) Во всех случаях вы используете личное наследование, так что на самом деле вы вообще не имеете дело с "интерфейсами". Доступ к государственному наследованию осуществляется следующим образом:
class SubInterface : public SuperInterface
2.) Вы используете =0
для функции, которую вы, по-видимому, хотите реализовать.
Это исправит ошибки компилятора, но дизайн по-прежнему вызывает сомнения. Учитывая мотивацию, которую вы дали в конце вашего вопроса, я рекомендую состав, а не (публичное) наследование. В C++, чтобы поделиться функциональностью, лучше всего выразить с композицией. Короче говоря, инкапсулируйте обычно используемые функции в отдельный класс и оборудуйте другие классы объектом этого.
class CommonFunctionality
{
//...
public:
void doSomething();
void doSomethingElse();
};
class SuperClass
{
//...
private:
CommonFunctionality m_functionality;
};
class SubClass : public SuperClass
{
//...
private:
CommonFunctionality m_functionality;
};
На самом деле, возможно, вам даже не нужно создавать класс для CommonFunctionality. Возможно, будут выполняться простые автономные функции. Программисты с фоном Java (и ваш код немного похожи на него) склонны вкладывать слишком много вещей в классы, чем это необходимо в C++.
Есть 2 проблемы, которые четко сформулированы компилятором:
Проблема 1
SubInterface::doSomethingElse()()
в Subclass
объявляется чистым виртуальным, не SubInterface::doSomethingElse()()
, что вы пытаетесь определить его в исходном файле (я уверен, что это ошибка копирования).
class Subclass : SubInterface
{
public:
Subclass();
virtual ~Subclass();
void doSomethingElse() = 0; // still pure?
};
Решение очевидно:
class Subclass : SubInterface
{
public:
Subclass();
virtual ~Subclass();
virtual void doSomethingElse() override
{
}
};
(здесь используется спецификатор override
C++ 11, поэтому компилятор проверяет правильность переопределения, не обязательно)
Проблема 2
doSomething()
даже не пытается переопределяться ни в SuperInterface
, ни в Subclass
, поэтому он остается чистым виртуальным. Хотя doSomething()
переопределяется в Superclass
, Subclass
не имеет понятия о существовании Superclass
.
Решение: переопределить doSomething()
либо в SuperInterface
, либо в Subclass
, либо в любом из дочерних Subclass
(их еще нет). Например, переопределение в Subclass
:
class Subclass : SubInterface
{
public:
Subclass();
virtual ~Subclass();
virtual void doSomething() override
{
}
virtual void doSomethingElse() override
{
}
};
Другие вопросы:
Вы наследованию без видимости спецификатора, то есть privat
Ely. Используйте public
наследование, пока вам не понадобится что-то еще:
class Derved : public Base {};
В ваших исходных файлах есть расширение .c
, но содержит код C++. Это может смутить некоторых компиляторов, если вы явно не укажете язык программирования с помощью аргументов командной строки. По соглашению большинство программистов используют расширение .cpp
, и большинство компиляторов обрабатывают такие файлы, как C++ исходные файлы.