在c++中,如何在基函数中访问派生类的成员?(这是正确的做法吗?)
In C++, how does one access members of a derived class in base functions? (Is this even the correct practice?)
我正在用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_;
};
如果需要访问基类函数实现中的成员,则"提升"这些成员仅为(受保护的)基类成员-您也需要在派生类中声明它们。在这两种情况下,基类和派生类函数都可以直接访问它们。
重建构造函数——由于构造函数调用的顺序,你只能初始化正在构造的实际类的成员,或者它的基类成员。您不能初始化派生类成员,因为它们还不存在!
这是一个宽泛的问题,需要你对继承有更好的理解。但有几件事你必须先知道:
如果你继承了一个类(超类),并且超类有一些具体的方法,你不需要在子类中再次实现它们,除非你想要对那个子类有一个不同的实现。这叫做重写那个方法
如果在子类中有一个被重写的方法,将使用哪些类成员和方法完全依赖于调用该方法的对象的声明:
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
在每个特定算法(每个派生类)中应该被覆盖的基类方法应该声明为virtual
。这种方法有时被称为模板方法模式。至于成员数据,尽管你可以在派生类中访问基类受保护的数据成员,而且这通常是编写代码的最快方式,但最干净的方法是不直接从基类访问成员数据,而是使用成员函数来访问或操作这些数据,并调用这些函数。这源于封装OO原则。请参阅:使成员变量受到保护是一种好做法吗?
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++图书指南和列表》中挑选一本初学者指南,并至少阅读与类和继承相关的章节
关于构造函数,它们几乎遵循相同的封装原则:类的构造函数负责初始化在该类层次结构的该级别定义的成员数据。您应该注意以下几点:
- 正如您所注意到的,它们只能初始化在其类继承层次结构级别上定义的数据成员。参见用初始化列表初始化父级's受保护成员(c++)你应该避免在构造函数中调用任何虚函数。参见:在构造函数内部调用虚函数
- 在基类中调用的 VIrtual 基函数,C++
- 从带有参数的基函数指针调用基方法
- 调用基函数时模板参数推导失败
- 为什么在此 CRTP 基函数调用中添加引用会消除错误?
- 用私有基函数覆盖公共虚拟函数
- 模板指针指向会员基函数
- 为什么在派生隐藏基函数时使用虚函数
- 从派生类调用基函数时会发生什么情况
- C++ 调用覆盖函数会导致调用基函数
- 使用继承的函数而不是基函数(不是抽象函数)
- 通过指针对基函数进行限定 id 调用
- C++如何确保在使用虚拟继承时调用继承的函数而不是基函数
- 从派生类函数调用基函数
- 导数使用基函数而不是自己的函数
- c++错误:基函数受保护
- 虚函数陷阱和使用基函数
- 在覆盖函数中使用基函数
- 为什么 clang 不允许派生类调用受保护的基函数?
- 带有模板返回类型的虚基函数:编译器在使用pointtype作为模板参数的派生类时失败(MSVC 2013)
- 基指针中的派生对象如何调用基函数