Альтернативы абстрактному базовому классу для хранения
Я задаюсь вопросом о доступных альтернативах полиморфизму времени выполнения, в частности, альтернативе наличию общего базового класса между моими классами для хранения и взаимодействия с экземплярами производных классов. RTP имеет недостатки, связанные с косвенным обращением через поиск в vtable, а также заставляет производные классы храниться в виде указателей, и, как правило, они должны выделяться динамически. Из того, что мне сказали, это мешает встраиванию и оптимизации компилятора.
Короче говоря, я не хочу этого:
class Animal
{
public:
virtual void noise() const = 0;
};
class Dog : public Animal
{
public:
virtual void noise() override const {std::cout<<"Woof!\n";};
};
class Cat : public Animal
{
public:
virtual void noise() override const {std::cout<<"Meow!\n";};
};
//...
std::vector<Animal*> animals;
Я хочу что-то вроде этого:
class Dog
{
public:
void noise() const {std::cout<<"Woof!\n";};
};
class Cat
{
public:
void noise() const {std::cout<<"Meow!\n";};
};
//...
std::vector<*Things that have noise()*> animals;
1 ответ
В вашем примере я не могу вам помочь, но для классов с членами данных, здесь есть оптимизация.
Давайте предположим, что мы хотим создать игру пули-ада.
В наивном подходе нам нужен класс Bullet
:
class Bullet {
public:
void draw(Renderer& r);
virtual void update_logic(float delta);
void run_physics();
protected:
float vx, vy; //velocity
private:
float x, y; //position
//...
};
Итак update_logic
метод будет вызван, чтобы установить скорость пули, затем run_physics
применит эту скорость к позиции.
Модификация логики требует полиморфизма, поэтому ваша коллекция становится std::vector<Bullet*>
(или же std::vector<std::unique_ptr<Bullet> >
в современном с ++).
Затем, в вашем основном цикле, сначала вы обновляете логику, затем физику, затем вы рисуете свои пули.
Конечно, это неэффективно из-за разыменования указателя, vtables и т. Д.
Вместо этого вы можете:
class Bullet {
public:
void draw(Renderer& r);
void update_logic(float delta) { logic->update(delta); };
void run_physics();
void setVelocity(float, float);
void setLogic(BulletLogic*);
private:
float x, y, vx, vy;
BulletLogic* logic;
};
Так что теперь вы можете использовать std::vector<Bullet>
и перенаправление будет происходить только при обновлении логики, но не при запуске физики или рисовании маркера.