Reputation: 6613
I have a base class of a geometric object which I use on its own but I also want to inherit the class into another one thats sort of an advanced version of the object since they share a lot of logic. The base object has several static creation methods (cannot use new due to argument conflicts), I don't want to inherit those. Can I specify somehow that those are not to be inherited?
EDIT: to include an example
struct Banana {
float length;
Banana() {}
Banana(float length) {
this->length = length;
}
static Banana CreateByHalfLength(float halfLength) {
return Banana(halfLength * 2);
}
};
struct AdvancedBanana : Banana {
float bendAmt;
AdvancedBanana(float length, float bendAmt) {
this->length = length; this->bendAmt = bendAmt;
}
};
I don't want AdvancedBanana::CreateByHalfLength to exist while I do want Banana::CreateByHalfLength to exist and be accessible from outside the class.
Upvotes: 5
Views: 1773
Reputation: 69988
If you want to automatically restrict the derived classes to overload the static Banana CreateByHalfLength(float halfLength);
then a very quick way is to encapsulate that function inside a virtual final
method.
e.g.
struct Banana {
...
// Create a namesake wrapper for the `static` function and make it final
virtual
Banana CreateByHalfLength(float halfLength) final {
return CreateByHalfLengthImpl(halfLength);
}
static Banana CreateByHalfLengthImpl(float halfLength) {
return Banana(halfLength * 2);
}
};
With this arrangement, now any derived class will not be able to create a similar function static
or non-static
.
Here is a demo.
The drawback of this approach is that you are adding a function overhead of being virtual
as well as being called with an unused object.
Upvotes: 1
Reputation: 443
What you are saying is a bit vague and an example would be useful. In particular since you use the word static, and it is not clear what the context of it is.
You cannot stop a derived class from inheriting all the methods of a base class. The best you can do is to make it a nonmember requiring the base object parameter. Then you would have to downcast the object before calling but you could still call it.
What you are suggesting seems to be a violation of the Liskov substitution principle. Which means you should rethink your design.
Also instead of B inheriting from A. You may want a base class Q from which both A and B derive from. [1]
[1] Q is a joke, some bible scholars think that there is some common unfound book they call Q which each gospel is copied from.
Edit: Additional.
With the example some things are clearer. Let me make some basic corrections to your understanding of C++. You said, you have several static creation methods because of argument conflicts. I think a better to say overloading can't resolve the different construction methods. The answer to that is very simple: expand the overload in one of two ways. Use enums, or use classes. The first you can see in streams calling read/append/read-write type streams by appending ios::ate etc.
In your case:
enum BCT {halfLength,fullLength,quarterLength ...};
Then do static Banana Create(float size,BCT type=fullLength) { switch(type) { case fullLength: return Banana(size); case halfLength: return Banana(size*2); case quarterLength: return Banana(size*4); ... } }
the alternate version is to use classes to distinguish parameter types ( I believe James Coplien called these exemplars )
class FullLength
class HalfLength
class QuarterLength
Then:
static Banana Create(float length); // Full length
static Banana Create(float halfLength, HalfLength &dummy);
static Banana Create(float quarterlength, QuarterLength &dummy);
The new classes add nothing in overhead, but do remove the overload ambiguity. I believe boost/std::filesystem uses this way for it's directorty iterators.
Having said that, once you decide how to create instances, these do not have to be static members. They can be ordinary constructors and that will solve your problem. For the most part. You still won't be able to stop AdvancedBanana from implementing a half length create method but the coder will be aware that he is doing it.
A brief note about statics, static member functions are ones that do not access the this pointer or what some languages call self, ie they do not access the members of a particular instance. In fact in preC++98 days, before they had statics, what people did was to do something like: ((Banana *)NULL)->static_function(arguments);
In your example, it is better to use constructors in the first place. static constructors are better left for things like factories where they are really needed.
Also, in Mido's response the line: a.CreateByHalfLength(1);
may or may not compile, I use so many many languages, I sometimes get confused by what is illegal :( but is shows bad thinking. The proper way to call a static would be Banana::CreateByHalfLength(1); not depending on an instance.
Upvotes: -1
Reputation: 164
You can only do like this, using private inherit for AdvancedBanana.
#include <stdio.h>
struct Banana {
float length;
Banana() {}
Banana(float length) {
this->length = length;
}
static Banana CreateByHalfLength(float halfLength) {
return Banana(halfLength * 2);
}
};
struct AdvancedBanana : private Banana {
float bendAmt;
AdvancedBanana(float length, float bendAmt) {
this->length = length; this->bendAmt = bendAmt;
}
};
int main()
{
Banana b;
b.CreateByHalfLength(1);
AdvancedBanana bb(1, 2);
//bb.CreateByHalfLength(2);
return 0;
}
AdvancedBanana::CreateByHalfLength should be exist, if you want Banana::CreateByHalfLength to exist and be accessible from outside the class. And also this is not a good solution.
On another way I am suggested, to design two or more classes or to take the functions out of Banana, for your demand. It will be something like this.
#include <stdio.h>
struct Banana {
float length;
Banana() {}
Banana(float length) {
this->length = length;
}
};
static Banana CreateByHalfLength(float halfLength) {
return Banana(halfLength * 2);
}
struct AdvancedBanana : private Banana {
float bendAmt;
AdvancedBanana(float length, float bendAmt) {
this->length = length; this->bendAmt = bendAmt;
}
};
int main()
{
Banana b = CreateByHalfLength(1);
AdvancedBanana bb(1, 2);
//bb.CreateByHalfLength(2);
return 0;
}
Upvotes: 1
Reputation: 1112
Try this redclare the function as private in the child:
#include <iostream>
class Banana {
public:
float length;
float getLenght(){
return length;
}
void setLenght(float value){
length = value;
}
Banana() {}
Banana(float length) {
this->length = length;
}
static Banana CreateByHalfLength(float halfLength) {
return Banana(halfLength * 2);
}
};
class AdvancedBanana : public Banana {
public:
float bendAmt;
AdvancedBanana(float length, float bendAmt) {
this->length = length; this->bendAmt = bendAmt;
}
private:
static AdvancedBanana CreateByHalfLength(float halfLength);
};
int main()
{
// work
Banana a(1);
a.CreateByHalfLength(1);
AdvancedBanana b(0,1);
//will fail
// b.CreateByHalfLength(1);
};
Upvotes: 3