Reputation: 966
Good day,
I am unsure how to describe the process I am inquiring about properly in one sentence so please excuse the title. I was searching for a way to ensure that users of a base class and or interface would assign data that would be regarded by the object itself and other objects in a non - default manner. So I have been doing the following:
struct ExampleInterface {
virtual void SomeMethod() = 0;
virtual std::string WhatLooksLikeAGetterButIsNot() = 0;
};
Here is a real world example:
//So states can be "poped in and out".//
struct State
{
//To retrive what the active state is called.//
/*Code In Question--->*/virtual std::string RegardStateAs() = 0;/*<---Code In Question*/
virtual void ExecuteState( VRGE::MDNode* metaData ) = 0;
};
The idea would be to eventually do something such as A (This option allows the problem trying to be prevented to occur if someone derives from "Update"):
struct Update : public State
{
//Yadda yadda...//
/*Code In Question--->*/std::string RegardStateAs() {
return std::string{ "Update" };
}/*<---Code In Question*/
};
B (This option does not allow what A does):
struct Update : public State
{
//Yadda yadda...//
//Not a good example, but the point gets across.//
Update( std::string value ) {
stateName = value;
}
/*Code In Question--->*/virtual std::string RegardStateAs() {
return stateName;
}/*<---Code In Question*/
private:
std::string stateName;
};
My question is: is this good or bad practice?
-----EDIT-----:
I do not have access to a compiler which can compile this, however it was pointed out to me that "override" would be perfect in this situation, for example:
//So states can be "poped in and out".//
struct State
{
//To retrive what the active state is called.//
/*Code In Question--->*/virtual std::string RegardStateAs() = 0;/*<---Code In Question*/
virtual void ExecuteState( VRGE::MDNode* metaData ) = 0;
};
struct Update : public State
{
//Yadda yadda...//
/*Code In Question--->*/std::string RegardStateAs() override {
return std::string{ "Update" };
}/*<---Code In Question*/
};
Upvotes: 4
Views: 2378
Reputation: 2906
As others have pointed out, your question is really addressing "overriding" (in the polymorphic sense), and not "overloading".
Assuming you want a fixed label for every subclass of State (static), your strategy works, but there are some limitations. For clarity, I'm going to refer to your "RegardAsState" as "ToString". If ToString needs to be called in the constructor of base class State, you'll have problems. This is because the v-table entry of ToString for a derived class such as Update has not yet been established when executing a base class constructor. A similar problem exists for calling ToString in State's destructor.
An alternative is to simply implement the state label as a const std::string in the base class State, and return it by const std::string & via a public get accessor. It's a bit more efficient, both in eliminating the v-table indirection, and permitting return by const ref; and it eliminates the aforementioned ctor/dtor issues in State.
Even the suggestion above is not optimal, since there's no need to make this an instance method if the label is the same for every distinct State subclass. If you want to get exotic, you could implement ToString as a static (stateless) accessor. This can be done with a little known trick of passing a constant string as a template parameter like so:
template<const std::string & label>
struct State
{
static const std::string & ToString()
{
return label;
}
};
extern const std::string UpdateLabel = "Update";
State<UpdateLabel> Update;
// Or, with a bit of help from the macro preprocessor
#define DEFINE_STATE(stateClass) \
extern const std::string stateClass##Label = #stateClass; \
struct stateClass##State : State<stateClass##Label> \
// continue with definition of stateClass##State ... {}
DEFINE_STATE(Sample)
{
// implementation of SampleState
};
Upvotes: 3