наследование — как в C ++ получить доступ к членам производного класса в базовых функциях? (Это даже правильная практика?)

Я пишу физическую программу на C ++, чтобы приблизить диапазон объекта, используя несколько алгоритмов. Я объявил базовый класс Simulation, который содержит как конкретные, так и абстрактные методы. (Например, функция для аппроксимации диапазона является чисто виртуальной, в то время как функция для получения следующей позиции является конкретной, поскольку она одинакова независимо от алгоритма.) Чтобы определить эти конкретные методы, я объявил все переменные в базовом классе. также. В производных классах я определяю как абстрактные методы базового класса, так и новые, специфичные для алгоритма; это требует, чтобы я определил членов как в базовом классе, так и в производных классах.

Проблема в том, что конкретные методы, определенные в базовом классе, обращаются к членам базового класса, а не к переопределенным, определенным в производных классах. Есть ли способ сделать это, или мой подход сам по себе неверен?

У меня также есть аналогичная проблема с конструкторами; вызов базового конструктора из конструктора производного класса инициализирует базовые члены.

В любом случае, спасибо за ваше время. Хотя я не совсем новичок в C ++, я склонен использовать его больше как C; Я довольно неопытен с объектно-ориентированными понятиями. Я мог бы сделать очень элементарную ошибку (или, скорее, недостаток дизайна), но я не нашел ничего подобного или получил какие-либо положительные результаты от тестирования, кроме переопределения методов в производных классах. (что я предполагаю, что я должен избегать)

Обновление: хотя защищенные члены работали, (спасибо Ozraptor; не совсем уверен, как я это пропустил) по просьбе R Sahu я выложу часть своего (теперь обновленного) кода:

Базовый класс:

class Simulation {
public:
Simulation();
Simulation(float, float, float, float, float, float, float, float, float);
bool IsInitialized(),
set_air_density(float),
set_delta_time(float),
set_drag_coefficient(float),
set_mass(float),
set_reference_area(float);
float next_x_position();
void set_next_x_position(float),
set_next_x_velocity(float),
set_next_y_position(float),
set_next_y_velocity(float);
protected:
static const float gravitational_acceleration_;
virtual bool SimulateRangeOrDie() = 0;
virtual void GetNextVelocity() = 0,
GetNextXVelocity() = 0,
GetNextYVelocity() = 0,
InitializeConstant() = 0;
void GetNextPosition(),
GetNextXPosition(),
GetNextYPosition(),
PushBackPositionVelocity();
bool x_position_initialized_,
x_velocity_initialized_,
y_position_initialized_,
y_velocity_initialized_;
float air_density_,
current_x_position_,
current_x_velocity_,
current_y_position_,
current_y_velocity_,
delta_time_,
drag_coefficient_,
constant_,
mass_,
next_x_position_,
next_x_velocity_,
next_y_position_,
next_y_velocity_,
reference_area_;
};

Один из производных классов:

class MomentumSimulation : public Simulation {
public:
MomentumSimulation();
MomentumSimulation(float, float, float, float, float, float, float, float,
float);
virtual bool SimulateRangeOrDie();
private:
virtual void GetNextVelocity(),
GetNextXVelocity(),
GetNextYVelocity(),
InitializeConstant();
void GetNextMomentum(),
GetNextXMomentum(),
GetNextYMomentum(),
Initialize(),
InitializeMomentum(),
InitializeXMomentum(),
InitializeYMomentum(),
PushBack(),
PushBackMomentum();
float current_x_momentum_,
current_y_momentum_,
next_x_momentum_,
next_y_momentum_;
};

0

Решение

Если необходимо получить доступ к членам из реализаций функций базового класса, «продвигать» эти члены только как (защищенные) члены базового класса — вам также необходимо объявить их в производных классах. Функции базового и производного классов могут обращаться к ним напрямую в обоих случаях.

Re конструкторы — из-за порядка вызова конструктора, вы можете только инициализировать членов создаваемого фактического класса или членов базового класса. Вы не можете инициализировать производные члены класса, так как они еще не существуют!

2

Другие решения

Это довольно широкий вопрос, требующий лучшего понимания наследования. Но вот несколько вещей, которые вы должны знать в первую очередь:

Если вы наследуете класс (суперкласс), а суперкласс имеет несколько конкретных методов, вам НЕ нужно снова их реализовывать в подклассах, если, конечно, вы не хотите иметь другую реализацию, специфичную для этого подкласса. И это называется переопределением этого метода.

Если в вашем подклассе есть переопределенный метод, то, какие члены класса и методы будут использоваться, полностью зависит от объявления объекта, для которого вы вызвали метод:

SuperClass a = new SuperClass();
a.someMethod(); //definitely the one in the superclass is called

SuperClass b = new SubClass();
b.someMethod(); //the one in the SuperClass called
((SubClass)b).someMethod(); //the one in SubClass called

SubClass c = new SubClass(); //trivial
0

Методы базового класса, которые должны быть переопределены в каждом конкретном алгоритме (каждый производный класс), должны быть объявлены virtual, Этот подход иногда называют Шаблон шаблона.
Что касается данных-членов, даже если вы можете получить доступ к защищенным элементам данных базового класса в производных классах, и зачастую это самый быстрый способ написания кода, самый простой способ сделать это — не обращаться напрямую к данным-членам базового класса, а иметь функции-члены, которые обращаются к этим данным или манипулируют ими, и вместо этого вызывают их. Это вытекает из принципа ОО инкапсуляции. Увидеть: Это хорошая практика, чтобы сделать переменные-члены защищенными?

Чтобы дать вам немного надуманный пример (C ++ 11), вот базовый класс моделирования:

using namespace std;
using namespace chrono;

class Simulation {
public:
// this is the same across all simulations:
void run() {
recordStartTime();
bool result = actuallySimulateStuff();
printSimulationResult(result);
}

protected:
// virtual methods should be overridden in derived classes
// this one has to be
virtual const string& name() const = 0;
// this one may not be, but if you don't, the simulation isn't going tobe very interesting
virtual bool actuallySimulateStuff() { return false; }

// these methods, like run() above, are invariant for all inherited classes
void recordStartTime() {
start_time_ = system_clock::now();
}
const start_time_& start_time() const {
return start_time_;
}
void printSimulationResult(bool result) {
auto end_time = system_clock::now();
auto durationInMS = duration_cast<milliseconds>(end_time - start_time_);

cout << "Simulation: '" << name() << "'";
cout << (result ? "Succeeded" : "Failed");
cout  << " in " << durationInMS << "ms.";
}

private:
system_clock::time_point start_time_ {};
};

А вот конкретный класс симуляции:

class TransmogrifySimulation : public Simulation {
protected:
// virtual methods should be overridden in derived classes
virtual const string& name() const {
static const string name_ = "TransmogrifySimulation";
return name_;
}

virtual bool actuallySimulateStuff() {
// simulate transmogrification here...
...
// which implies, at some point:
someSimulationDetail();
...

return result_;
}

void someSimulationDetail() {
// this is a really weird, unreliable simulation
auto currentRunTime = duration_cast<milliseconds>(system_clock::now() - start_time());
result_ = 0 != (currentRunTime % 2);
}

private:
bool result_ = false;
};

Это всего лишь пример, но если вы хотите чувствовать себя комфортно с концепциями, которые он использует, я настоятельно рекомендую вам взять руководство для начинающих из Полное руководство и список книг C ++ и прочитайте хотя бы главы, связанные с классами и наследованием

Что касается конструкторов, они в значительной степени следуют одному и тому же принципу инкапсуляции: конструктор класса отвечает за инициализацию данных-членов, определенных на этом уровне иерархии классов. Вы должны отметить несколько вещей:

0
По вопросам рекламы [email protected]