Как реализовать виртуальные методы в файлах реализации?

Вопрос о С++:

позволяет сказать, что я хочу использовать абстрактный класс с несколькими методами в качестве интерфейса:

// this is AbstractBase.hpp

class AbstractBase
{
public:
  virtual int foo() = 0;
  virtual int bar() = 0;
  // imagine several more pure virtual methods are here
};

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

Должен ли я снова объявлять все эти методы в объявлениях подкласса, как это? Я думаю, что должен быть способ избежать этого шага (из ObjC, Java)

// this is Concrete.hpp

class Concrete : public AbstractBase
{
public:
  int foo();
  int bar();
  // need to copy paste all the method declarations here again...? why?
};

то, что я хотел бы сделать, это реализовать методы в файле реализации следующим образом:

// this is Concrete.cpp

#include "Concrete.hpp"

int Concrete::foo()
{
    return 666;
}

// etc...

но не может понять, как без повторного объявления интерфейса в каждом подклассе.

Ответ 1

Несмотря на то, что вы можете избежать записи заголовка заголовка функции во второй раз, если вы объедините определение с объявлением "Java-style", т.е.

class Concrete : public AbstractBase
{
public:
  int foo() override {return 666;}
};

С++ не позволяет избежать шага объявления, если вы хотите предоставить свое определение отдельно.

Причиной этого является то, что объявление является первичным, а определения - вторичными. Чтобы иметь возможность писать

int Concrete::foo()
{
    return 666;
}

в вашем файле CPP вам нужно сообщить компилятору, что Concrete имеет намерение переопределить foo в своем заголовке.

Ответ 2

Во-первых, как сказал LogicStuff в своем комментарии, вы делаете это правильно.

Во-вторых:

//нужно снова скопировать все декларации методов здесь...? почему?

потому что ваш подкласс

  • не нужно реализовывать все (или даже любые) функции виртуального базового класса. Он может переопределить только некоторые из них и оставить других в более производном классе

  • разрешено добавлять другие методы и перегрузки.

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

Ответ 3

Если вам нравится базовый класс abstrct, вы должны сделать свои методы пустым виртуальным (только объявление без реализации (напишите =0 за объявлением вашего метода):

class AbstractBase
{
public:
  virtual int foo() = 0; // <- = 0 
  virtual int bar() = 0; // <- = 0
};

Используйте ключевое слово override, поэтому вы получите ошибку компиляции, если декалирование изменений mehtod в базовом классе:

Concrete.hpp

class Concrete : public AbstractBase
{
public:
  int foo() override; // <- override
  int bar() override; // <- override
};

Завершите реализацию своих методов, как вы сделали это в своем вопросе:

Concrete.cpp

#include "Concrete.hpp"

int Conrete::foo()
{
    return 666;
}

int Conrete::bar()
{
    return 777;
}

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

Ответ 4

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

Ответ 5

Вы должны объявить все методы, которые вы хотите переопределить в подклассе.

Вы можете использовать макрос, чтобы упростить это, когда это делается часто. Определите макрос в файле заголовка базового класса следующим образом:

#define ABSTRACTBASE_OVERRIDE_ALL \
    int foo() override; \
    int bar() override;