views:

126

answers:

3

I have an abstract C++ class with no constructor. It's supposed to be a base class so other classes can inherit from it. What I am trying to do is to declare a constant variable in the base class and initialize it in each derived class' constructor but nowhere else in each one of those classes. Is it legal in C++? If so, how can I do that?

+8  A: 

Is it legal in C++?

No. The constant must be initialized in the base class constructor.

The solution is to provide an appropriate constructor in your base class – otherwise it cannot be used. Furthermore, there’s no reason not to provide that constructor.

class Base {
    int const constant;
public:
    virtual ~Base() = 0; // Makes this an abstract base class.
protected:
    Base(int c) : constant(c) { }
};

// Must be implemented!
Base::~Base() { }

class Derived : public Base {
public:
    Derived() : Base(42) { }
};
Konrad Rudolph
If you make the base class constructor `protected` then you don't have to worry about anyone else instantiating it directly.
Zooba
I thought if there is a constructor in an abstract base class, it is no longer abstract anymore because it can be instantiated. Am I wrong?
@Zooba: Correct. See my updated code. ;-) Additionally, there are other ways to make a base class abstract (i.e. uninstantiable). The standard way is to provide a pure virtual function (e.g. conveniently the destructor, which **must** be virtual anyway).
Konrad Rudolph
@user246392: Yes you are wrong. Look at my code: the constructor is protected, so the class cannot be instantiated directly. What’s more, there is a pure virtual function so even with a public constructor, the class can’t be constructed by itself.
Konrad Rudolph
very smart. thanks a lot. :)
+1, yep yep. Note that if you're going to implement `Base::~Base` in the header file, it should be `inline virtual`.
Potatoswatter
@Potatoswatter Does it need to be explicitly marked as inline?
Pedro d'Aquino
@Pedro: Any function defined in a header must be inline per the one-definition rule. (Or in an anonymous namespace.) This function won't be inline unless explicitly marked, so yes.
Potatoswatter
@Potatoswatter The `inline` declaration is only a guide to the compiler. Without it, you may get one definition per #include, or the compiler may inline it and give you one definition per use. Either way, with an empty method block, there are no ill-effects. (Hopefully the compiler will optimise the call out completely.)
Zooba
@Zooba: No, `inline` is required. It is not a performance hint; it changes the meaning of the declaration. The function body being empty does not change anything. http://stackoverflow.com/questions/1875947/benefits-of-declaring-a-function-as-inline
Potatoswatter
@Potatoswatter True. I didn't realise the consequences of the implicit `inline` when providing an implementation within the class definition. However, since no diagnostic is required (quotes @ http://stackoverflow.com/questions/908830#910686), the end result will likely be as I described, even if "exactly one" is violated, but there would be no ill effects since the method doesn't do anything.
Zooba
@Zooba: Since a destructor doesn't have a name, its address may not be taken, so I suppose that's pretty much true. For a named inline pure virtual function, though, taking its address in two different source files should produce a multiple definitions linker error. (Unless the linker is friendly enough to merge the identical definitions, which is really a mixed blessing.)
Potatoswatter
@Zooba: It’s not only a problem of diagnostics. The linker won’t know what to do with the multiple definitions of the same symbol. What is it supposed to do? It doesn’t necessarily know that they really refer to the same method, so silently suppressing one of the definitions may be really dangerous. So it complains.
Konrad Rudolph
@Konrad Okay, so I did what I should've done in the first place and tested it... error LNK2005 (in VC10) for "already defined" which marking it `inline` fixes. Looks like I learned an important use for `inline` along with some humility at the same time. (Gotta respect the efficiency of that, I guess...)
Zooba
A: 

Why don't you make a protected constructor in the abstract base class and set the constant value there?

ur
+1  A: 

If at all you need to do it this way:

struct Base {
    Base( std::string const & someValue )
    : const_value( someValue ) {
    }
protected:
   std::string const const_value;
};

struct Derived : Base {
    Derived()
    : Base("Derived"){
    }
};

The reason for this is that you are only allowed to assign values to a const value at initialization time. Once Base is initialized you are no longer allowed to modify the value. Therefore you have to do it that way as shown above.

Vinzenz