Мне было интересно, если есть так или иначе, что мой суперкласс может вызвать функцию initValues () для подкласса без необходимости переопределить конструктор?
Вот код:
#ifndef VECTOR_MATH_H
#define VECTOR_MATH_H
#include "GL\glew.h"
#include <iostream>
namespace Math3d
{
class Vector
{
public:
Vector(int length=2) : v(new float[length]) { initValues(); }
~Vector() { delete[] v; }
protected:
virtual void initValues()
{
std::cout << "Vector" << std::endl;
}
float* v;
};
class Vector3 : public Vector
{
public:
protected:
void initValues()
{
std::cout << "Vector3" << std::endl;
}
};
}
#endif
Затем я создаю переменную следующим образом:
Vector3 vec;
И тогда я хотел бы
initValues ()
метод подкласса Vector3 для вызова.
Это возможно?
Краткий ответ: нет, вы не можете.
Длинный ответ: виртуальная таблица объекта не раскрывается, пока не будет вызван конструктор производного класса. В конструкторе базового класса виртуальная таблица указывает на реализацию функции базового класса. Если базовый класс имеет реализацию, эта функция будет вызвана. Если базовый класс не имеет реализации, возникает ошибка / исключение, зависящее от платформы.
Если вы хотите назвать оба суперкласса initValues()
а затем подкласс initValues()
вам нужно будет явно позвонить Vector::initValues()
от Vector3::initValues()
поскольку динамическая диспетчеризация всегда будет вызывать более специализированную версию метода:
void Vector3::initValues() {
Vector::initValues();
other specific code;
}
Если вы действительно хотите сохранить порядок в нужном вам порядке, вам потребуется второй метод:
class Vector {
protected:
void initValues() {
// common init
specificInitValues();
}
virtual void specificInitValues() = 0;
};
class Vector3 : public Vector {
protected:
virtual void specificInitValues() override {
// specific init
}
};
Вы не можете сделать это с динамическим полиморфизмом (используя virtual
таблица функций, ака. как виртуальные таблицы) из конструктора, потому что в момент, когда суперкласс пытается вызвать виртуальный метод, только суперкласс построен, а подкласс initValues()
реализация не может быть вызвана из полностью построенного vtable.
Есть два способа преодолеть эту проблему:
1. Сделай свой initValues()
метод public и требует, чтобы он вызывался от клиентов после создания
2. Вы можете сделать, чтобы добиться такого поведения, вместо этого использовать статический полиморфизм:
template<class Derived>
class VectorSuper
{
public:
VectorSuper(int length=2)
: v(new float[length])
{
static_cast<Derived*>(this)->initValues();
}
~VectorSuper() { delete[] v; }
protected:
void initValues() // Note, no virtual
{
std::cout << "VectorSuper" << std::endl;
}
float* v;
};
class VectorSub
: public VectorSuper<VectorSub>
{
protected:
void initValues() // Note, no virtual
{
VectorSuper<VectorSub>::initValues();
std::cout << "VectorSub" << std::endl;
}
}
Последний способ может потребовать дополнительного разграничения абстрактного интерфейса, реализованного в суперклассе, для его разумного использования в контексте, который не знает о VectorSub
и не нужно.
class AbstractVector
{
public:
virtual ~AbstractVector() = 0;
// example interface
virtual float operator[](int index) const = 0;
};
template<class Derived>
class VectorSuper
: public AbstractVector
{
public:
VectorSuper(int length_=2)
: length(length_), v(new float[length])
{
static_cast<Derived*>(this)->initValues();
}
~VectorSuper() { delete[] v; }
virtual float operator[](int index) const
{
if(index >= length || index < 0)
{
throw std::invalid_argument("index");
}
return v[index];
}
protected:
// ... as before
int length; // Remember length additionally!
float* v;
};