I have a virtual class A
with data val
and val2
. val
is set by A
, but val2
is supposed to be set by children of A
(based on the value of val
). I would like to force every deriving class to set val2
. The following
#include<iostream>
class A {
public:
A(): val(1), val2(getVal2())
{};
int val;
int val2;
protected:
virtual int getVal2() = 0;
};
class B: public A {
protected:
virtual int getVal2() { return 2*val; };
};
int main(){
B b;
std::cout << b.val2 << std::endl;
}
does not work since the constructor of A
calls a function (getVal2
) which at the time isn't defined yet:
/tmp/cc7x20z3.o: In function `A::A()':
test9.cpp:(.text._ZN1AC2Ev[_ZN1AC5Ev]+0x1f): undefined reference to `A::getVal2()'
collect2: error: ld returned 1 exit status
What's a better way of forcing deriving classes to set val2
explicitly?
答案 0 :(得分:8)
Make val2
a parameter in the constructor of your base class (and don't use a default constructor):
class A
{
public:
A(int _val2) : val(1), val2(_val2) {};
A() = delete; //for clarity, not required as it is implicitly deleted
//...
int val;
int val2;
};
This requires derived classes to set val2
in their constructors:
struct B : public A
{
B() : A(0) {} //A must be initialized, thus val2 is set in any case
// ...
};
Don't try to access pure virtual members in the base class constructor: they are not yet constructed and thus not yet accessible; this yields undefined behaviour.
EDIT: from the comments it seems as if the problem were actually more complex than described in the OP. Namely: val
shall be usable in the derived class constructor initializer list (and shall not be static).
Imo the cleanest solution I came up with (for others see the comments) is to introduce another base class and derivevirtual
:
struct Abase
{
Abase() : val(1) {}
int val;
};
struct A : virtual Abase
{
A(int _val2) : val2(_val2) {}
int val2;
};
struct B : virtual A
{
B() : /* Abase() is called implicitly here, */ A(2*val){}
//^^^^^ now val is correctly initialized.
};
DEMO.
Further, you can consider to inherit protected
(since at least Abase
is an implementation detail and is not meant to be used polymorphically).
答案 1 :(得分:0)
在val
的初始化中可能使用val2
的事实使问题变得非常复杂。一种解决方案是将val2
传递给A
的构造函数,并创建一个托管Abase
的父类val
,并确保它首先在链中初始化。 / p>
另一个选择是在val2
中为A
提供一个纯虚拟的getter:
#include<iostream>
class A {
public:
A(): val(1)
{};
int val;
virtual int getVal2() = 0;
};
class B: public A {
public:
virtual int getVal2() { return 2*val; };
};
int main(){
B b;
std::cout << b.getVal2() << std::endl;
}