Force deriving class to override data member

时间:2015-06-15 15:01:36

标签: c++

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?

2 个答案:

答案 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;
}