Я хочу, чтобы несколько классов наследовались от этого интерфейса:
class IPlayer {
public:
virtual ~IPlayer() {}
virtual void doSomething() = 0;
protected:
std::string m_name;
};
Вот класс, который должен наследоваться от приведенного выше:
class Jack : public IPlayer {
public:
Jack(std::string t_name)
{
m_name = t_name;
}
~Jack() { }
void doSomething()
{
/* do a bunch of stuff */
}
};
Имейте в виду, что у нас есть другие классы, которые наследуются от IPlayer таким же образом, например Bob и Alice.
Теперь предположим, что я хотел создать контейнер для Jacks, Bobs и Alices, который позволил бы мне перегруппировать их в одной переменной. В таком состоянии для меня это невозможно, так как у IPlayer нет ctor, поэтому не может служить шаблоном для таких объектов, как векторы или списки. (по крайней мере я так понимаю)
Было бы лучше иметь класс между IPlayer и Jack, который реализовывал бы только ctor и dtor, а затем оставлял бы другие методы чисто виртуальными для использования детьми;
ИЛИ
Сделать IPlayer (и/или любой будущий интерфейс) наследником того же класса, что и выше, который реализует только ctor и dtor, просто чтобы сделать IPlayer ctorable для контейнеров?
@IgorTandetnik Верно, это самый правильный вариант. Почему/как использование контейнера указателей позволяет обойти эту проблему?
Обходит какую проблему? Я не уверен, что вполне понимаю природу трудности.
Если бы не ctor, что во внутренних функциях абстрактного класса делает его неспособным служить экземпляром для контейнеров?
Вы не можете создать экземпляр абстрактного класса, точка; будь то элемент контейнера или иным образом. В этом суть чисто виртуальных функций. Вы не можете иметь std::vector<IPlayer> по той же причине, по которой вы не можете писать IPlayer player; или IPlayer* player = new IPlayer;
Правильно, теперь намного понятнее. Все это связано с моим непониманием того, что могут или не могут делать абстрактные классы. Теперь я понимаю, что могу использовать указатели или ссылки на абстрактные классы, но не могу создать объект из абстрактного класса. Спасибо за терпеливость :)





На самом деле с этими классами проблем нет. IPlayer может быть абстрактным классом, но вам никогда не нужно создавать экземпляр этого класса. Вы можете прекрасно объявить вектор указателей на IPlayers, если все элементы вектора указывают на неабстрактные подклассы.
Jack* j = new Jack("jack");
std::vector<IPlayer*> v = {j};
Этот код прекрасно работает, так как экземпляр IPlayer не создается.
Хотя это правильно, важно понимать, что использование голых указателей не является хорошим подходом. Кто собирается удалить игрока Джека?
@ rm1948 Я использовал голые указатели, чтобы упростить свой код. Конечно, ОП может использовать интеллектуальные указатели, но это его выбор, поскольку я не могу знать, какой интеллектуальный указатель оптимален для его системы. Я просто показываю, как это можно реализовать.
IPlayer может иметь конструктор и фактически имеет его, предоставленный компилятором.
Вот один из подходов, при котором список игроков хранится в статическом элементе IPlayer. Но список не обязательно должен быть в IPlayer.
#include <memory>
#include <vector>
// forward declaration
class IPlayer;
using Players = std::vector<PlayerPtr>;
class IPlayer {
public:
virtual ~IPlayer() {
}
static void addPlayer(IPlayer& player) {
mPlayers.push_back(std::make_unique<IPlayer>(player));
}
private:
static Players mPlayers;
};
Players IPlayer::mPlayers;
class Bob : public IPlayer {
};
void run() {
Bob bob;
IPlayer::addPlayer(bob);
}
Вы бы использовали контейнер (возможно, умных) указателей на
IPlayer, как вstd::vector<IPlayer*>илиstd::vector<std::unique_ptr<IPlayer>>. У классаIPlayerуже есть конструктор — конструктор по умолчанию; причина, по которой его экземпляр не может быть создан, заключается в том, что он абстрактен. Добавление дополнительных конструкторов не изменит этот факт.