This is a common practice. In fact, some well-known design patterns rely on this, such as the Template Method Pattern. In a nutshell, this allows you to specify some aspects of the behavior you're describing through your class hierarchy as invariant, while letting other aspects of that behavior vary based on the specific type of instance you are referring to at a given point.
Whether or not it is a good or not depends on your precise use case: does it make sense for you to share the implementation of your float member data storage among all your base classes ? This is a bit hard to answer with the example you posted as the derived classes do not rely on my_float
in any way, but there are tons of cases where this makes sense and is a good way to split the responsibilities of your class hierarchy.
Even in cases where it does make sense to share implementation of details across classes, you have several other options, such as using composition to share functionality. Sharing functionality through a base class often allows you to be less verbose compared to sharing this functionality via composition, because it allows you to share both the implementation and the interface. To illustrate, your solution has less duplicated code than this alternative that uses composition:
class DataStorage {
private:
float data_;
public:
DataStorage()
: data_(0.f) {
}
void setFloat(float data) {
data_ = data;
}
};
class NotASubclass1 {
private:
DataStorage data_;
public:
void SetFloat(float value){ data_.setFloat(value); }
...
}
class NotASubclass2 {
private:
DataStorage data_;
public:
void SetFloat(float value){ data_.setFloat(value); }
...
}