This is a question about designing classes in C++.
My base class have virtual final method with parameters, the name of it is basically "update". In a child class, I wanted to use a higher level way to update the object, so I made a method called "update" too (for convenience), but this one have no parameter. And Clang warns me the method is hiding a base class method. In my point of view, I'm not totally agree with this, because the signature is different, it should just complete the overloading.
Is there a good reason to not do that ?
The warning is telling you that the function in the derived class makes it more difficult to call the original base class function on an object of the derived type:
class Base {
public:
virtual void update(int param) final;
};
class Derived : public Base {
public:
void update();
};
void f(Derived& obj) {
obj.update(2); // Error!
// Name lookup for update finds only Derived::update,
// which takes no arguments
}
To "unhide" the base class function, so that the derived class acts as though it has both overloads, use a using directive:
class Derived : public Base {
public:
void update();
using Base::update;
};
Related
I have code similar to the following:
template<class BASE_TYPE = COdbcQuery>
class CRemoteQuery : public BASE_TYPE
{
CRemoteDatabase m_Db;
public:
CRemoteQuery()
: BASE_TYPE(&m_Db)
{
}
~CRemoteQuery()
{
}
};
My problem is that m_Db.Open() must be called before passing m_Db to the base constructor.
If I call a method as an argument to the base constructor that calls Open(), it fails because m_Db has not yet been initialized.
I tried creating a virtual method in the base class, which would be called during initialization and this class could override, but template classes cannot override virtual methods.
Restructuring my base classes so that m_Db doesn't need to be opened first raises a lot of difficult issues. Is there no way to do this?
This sequence of events can be easily implemented by making a small design change:
class CRemoteDB {
protected:
CRemoteDatabase m_Db;
CRemoteDB()
{
m_Db.open();
}
};
template<class BASE_TYPE = COdbcQuery>
class CRemoteQuery : private CRemoteDB, public BASE_TYPE
{
public:
CRemoteQuery()
: BASE_TYPE(&m_Db)
{
}
~CRemoteQuery()
{
}
};
Parent classes always get constructed in declaration order. The CRemoteDB parent class gets constructed first, and CRemoteDatabase::open() gets called in the parent class's constructor.
Then BASE_TYPE gets constructed, and gets a pointer to the opened m_Db.
CRemoteQuery can access m_Db from its parent class no differently than it would be if it was its own class member.
but template classes cannot override virtual methods.
P.S. Whoever told you that was wrong. Template classes can certainly override virtual methods. I've got a massive hierarchy of templates here, all overriding virtual methods of their parent classes, left and right.
I have a base class with a bunch of functionality and a derived class that extends that class but there are a few methods in the base class that don't make sense on the derived class.
Is it possible to do something to prevent these method(s) from being used by the derived class?
Class A
{
...
public:
void SharedMethod();
virtual void OnlyMakesSenseOnA();
}
Class B : public Class A
{
...
public:
void OnlyMakesSenseOnB();
}
The following obviously doesn't work but is it possible to do something similar so that the compiler doesn't allow a certain base class method to be called?
Class B : public Class A
{
...
public:
void OnlyMakesSenseOnA() = 0;
}
No, and this is completely wrong. If the member function is not callable in the derived type you are breaking the Liskov Substitution Principle. Consider whether this is the correct inheritance relationship. Maybe you want to extract SharedMethod to a real base and provide two separate unrelated A and B types.
This isn't as easy of an answer as I had hoped, but a coworker suggested that this situation is an indication of bad design and that I should re-think my inheritance structure by adding a new base class that only contains common functionality:
Class Base
{
...
public:
void SharedMethod();
}
Class A : public Base
{
...
public:
void OnlyMakesSenseOnA();
}
Class B : public Base
{
...
public:
void OnlyMakesSenseOnB();
}
Edit: Thanks to #David for providing a name for the rule that I'm trying to break. B is not a "Behavioural Subtype" of A because it fails the "counterfeit test". Therefore, deriving B from A violates the Liskov Subtitution Principle.
According to this slide deck, the counterfeit test is as follows:
Suppose I promise to deliver you an object of class T, but
instead I give you an object x of class S.
You can subject x to any series of method calls you like
(chosen from T’s signature).
If x behaves in a way that is not expected of a T object,
then you know it is a counterfeit, x has failed the test.
If all S objects always pass every counterfeit test, then S is
a behavioural subtype of T.
You could also just throw an exception if the invalid method is called on the derived class. It doesn't catch the bug at compile time but at least it prevents it from accidentally being used a runtime.
Class B : public Base
{
...
public:
void OnlyMakesSenseOnA() { throw Exception(); }
}
Yes, it's possible and quite simple, if we're talking about an external call. You can hide parent's method with private methods of derived class. Works with the static methods as well.
Tested on cpp 98, 11, 14. Try yourself in C++ shell.
class Base{
public:
void methodBase(){};
static void methodBaseStatic(){};
};
class Derived : public Base{
//private: //(private on default)
void methodBase(){};
static void methodBaseStatic(){};
};
Normal operation:
int main()
{
Base b;
b.methodBase();
Base::methodBaseStatic();
Derived d;
return 0;
}
Compilation error
int main()
{
Derived d;
d.methodBase();
Derived::methodBaseStatic();
return 0;
}
I have a reference-counted base class which will be the parent of most of my objects. It has its own little reference counted methods. The trouble is, these might be subclassed. In a fashion, I'm trying to implement NSObject, but in C++.
This therefore means there's an 'init' method.
Therefore, the init method should always return the child's class type. Imagine this:
class BaseClass{
public:
virtual childclass* init();
}
I know in Objective-C this would be quite easy as it returns 'id' and no casting is required. However in C++ there's no such thing as 'id' and therefore I'm hoping there's a macro or something that automatically expands childclass* into the actual child's class. I'm quite new to C++ so I'm not sure how to implement this.
This can be done via the Curiously Recurring Template Pattern.
template <class childclass> class BaseClass
{
public:
virtual childclass* init();
};
class ChildClass : BaseClass<ChildClass>
{
public:
virtual ChildClass* init();
};
You should use C++'s constructors which return the proper type. It would work like this:
class BaseClass {
public:
BaseClass();
virtual ~BaseClass();
};
class childClass : public BaseClass {
public:
childClass();
virtual ~childClass();
};
Then you can simply call new childClass and you'll get a pointer to the childClass.
I don't understand why the compiler doesn't like this, here is and example of the problem:
class A
{
public:
virtual void Expand() { }
virtual void Expand(bool flag) { }
};
class B : public A
{
public:
virtual void Expand() {
A::Expand(true);
Expand(true);
}
};
When I try to compile this the A::Expand(true); compiles fine, but the non scoped Expand(true); gets this compiler error:
'B::Expand' : function does not take 1 arguments
You don't need virtual methods for that behaviour. Methods in a derived class hide methods with the same name in the base class. So if you have any function named Expand in your derived class (even if it is an override of a virtual method from the base class), none of the base classes methods with the same name are visible, regardless of their signature.
You can make the base classes methods visible with using. For that you would add using A::Expand; to the definition of B:
class B : public A
{
public:
using A::Expand;
virtual void Expand() { Expand(true); }
};
That's because besides overriding the base Expand(), you're also hiding the base Expand(bool).
When you introduce a member function in a derived class with the same name as a method from the base class, all base class methods with that name are hidden in the derived class.
You can fix this by either qualifying (as you have) or with the using directive:
class B : public A
{
public:
using A::Expand;
virtual void Expand() {
A::Expand(true);
Expand(true);
}
};
It must be something specific in my code, which I can't post. But maybe someone can suggest possible causes.
Basically I have:
class CParent
{
public:
void doIt(int x);
};
class CChild : public CParent
{
public:
void doIt(int x,int y,int z);
};
CChild *pChild = ...
pChild->doIt(123); //FAILS compiler, no method found
CParent *pParent = pChild;
pParent->doIt(123); //works fine
How on earth?
EDIT: people are talking about shadowing/hiding. But the two versions of doIt have different numbers of parameters. Surely that can't confuse the compiler, overloads in child class which can't possibly be confused with the parent class version? Can it?
The compiler error I get is:
error C2660: 'CChild::doIt' : function does not take 1 argument
You have shadowed a method. For example:
struct base
{
void method(int);
void method(float);
};
struct derived : base
{
void method(int);
// base::method(int) is not visible.
// base::method(float) is not visible.
};
You can fix this with a using directive:
class derived : public base
{
using base::method; // bring all of them in.
void method(int);
// base::method(int) is not visible.
// base::method(float) is visible.
};
Since you seem insistent about the number of parameters, I'll address that. That doesn't change anything. Observe:
struct base
{
void method(int){}
};
struct derived : base
{
void method(int,int){}
// method(int) is not visible.
};
struct derived_fixed : base
{
using base::method;
void method(int,int){}
};
int main(void)
{
{
derived d;
d.method(1, 2); // will compile
d.method(3); // will NOT compile
}
{
derived_fixed d;
d.method(1, 2); // will compile
d.method(3); // will compile
}
}
It will still be shadowed regardless of parameters or return types; it's simply the name that shadows. using base::<x>; will bring all of base's "<x>" methods into visibility.
You are hitting a classic problem. You need using CParent::doIt; in your CChild class. I'll scrounge up the duplicate questions.
Edit:
Here's my answer to essentially the same question: Overriding a Base's Overloaded Function in C++
I have never done this without having that method in the base class before. I think that adding "using CLASS::METHOD" in derived class will give you access to the other version of the overloaded method.
class CParent
{
public:
void doIt(int x);
};
class CChild : public CParent
{
public:
void doIt(int x,int y,int z);
using CParent::doIt;
};
The problem is CChild doesn't actually inherit from CParent.
And so it doesn't have a doIt method that takes only one argument.
When you override a function in the derived class, only that function in the derived class is visible to the user of that class. The base class version becomes hidden.
Therefore, your pChild pointer calling doIt(int x) will fail since you are using a derived class pointer to call the base class function. The pParent pointer calling doIt(int x) will work since you are using a base class pointer to call the base class function. Even though you have a child object being pointed to by a parent pointer (upcasted), the class type here is determined by the declaration of the pointer which is a CParent.
To be able to call that base class function using the derived class pointer, you can:
Qualify the base class name in the function call, as in the following:
pChild->CParent::doIt(123);
Use a using directive to bring the function name from the base class into the derived class, as seen in the previous posts.
I understand that this behavior is to give you flexibility to override the behavior of base class method in your derived class.
Lets assume that you have a function foo(int) in base class and you want to change the behavior of this function in your derived class. Now if the base class method is not hidden by your derived class method (which has same prototype as that of base class's method), it will introduce ambiguity in overload resolution.
The method in your child class has a different number of arguments than what you're trying to pass into it. Could it be related to that?