Public virtual function derived private in C++
The behavior is correct. Whenever you declare your function as "virtual", you instruct the compiler to generate a virtual call, instead of the direct call to this function. Whenever you override the virtual function in the descendant class, you specify the behavior of this function (you do not change the access mode for those clients, who rely on the "parent's" interface).
Changing the access mode for the virtual function in the descendant class means that you want to hide it from those clients, who use the descendant class directly (who rely on the "child's" interface).
Consider the example:
void process(const A* object) {
object->func();
}
"process" function relies on the parent's interface. It is expected to work for any class, public-derived from A. You cannot public-derive B from A (saying "every B is A"), but hide a part of its interface. Those, who expect "A" must receive a fully functional "A".
Why private virtual member function of a derived class is accessible from a base class
First, as @Eljay notes - printImpl()
is a method, albeit virtual, of the Base
class. So, it's accessible from the base class. Derived
merely provides a different implementation of it. And the whole point of virtual functions is that you can call a subclass' override using a base class reference or pointer.
In other words, private
only regards access by subclasses; it's meaningless to keep something private
from a class' base class: If a method is at all known to the base class, it must be a method of the base class... a virtual method.
Having said all that - note that the Derived
version of printImpl()
is effectively inaccessible from print()
- when it's invoked within the base class constructor. This is because during that call, the constructed vtable is only that of Base
, so printImpl
points to Base::printImpl
.
I thought private member functions could be called ONLY from the member functions of the same class
And indeed, print()
is a member of Base
, which invokes printImpl()
- another method of Base
.
If a private virtual function is overridden as a public function in the derived class, what are the problems?
Accessibility check is performed based on the static type of the object. The type of o
is One*
. This means that if One::func()
is private
, then o->func()
won't compile.
On the other hand, which virtual member function will be called (i.e. dynamic dispatch) happens at run-time, based on the dynamic type of the object. So if One::func()
is public
, o->func()
will call Two::func()
, because o
is pointing to an object of type Two
.
For your sample code and use case, making One::func()
private
is just meaningless. But note that there's a famous idiom called Non-Virtual Interface, which makes use of private
virtual member functions of base class.
Other suggestions:
- Don't forget to
delete o;
Add a virtual destructor in the base class
One
. Otherwisedelete o;
will lead to undefined behavior; e.g. the destructor ofTwo
might not be invoked.class One {
public:
virtual ~One() {}
// ...
};
C++ override private pure virtual method as public
According to https://en.cppreference.com/w/cpp/language/virtual#In_detail overriding a base's virtual
member function only care about the function name, parameters, const/volatile-ness and ref qualifier. It doesn't care about return type, access modifier or other things you might expect it to care about.
The linked reference also specifically notes that :
Base::vf does not need to be visible (can be declared private, or inherited using private inheritance) to be overridden.
Nothing that I can find explicitly gives permission to do this, but the rules of overriding do not prevent it. It's allowed by virtue of virtual
functions and function overriding existing and not disallowing this case.
If you are asking why this is how the language is, you may have to ask the standardization committee.
Override public virtual function with private base function?
Is there any way to make this work as I supposed it may have worked?
You should override the member function and explicitly call B::start()
:
class C: public A, private B {
public:
void start() override { B::start(); }
};
Why would the compiler accept this code as valid? As I see it there
are now twostart()
functions with the exact same signature in C and
yet the compiler seems fine with it and only callsA::start()
.
You are right, there are two member functions accessible in C (A::start()
and B::start()
). And in class C
, without overriding start()
or making the start()
of any of the base classes visible by doing a using ...::start()
, you will have ambiguity error when trying to call the member function using unqalified namelookup from an object of C
.
class A {
public:
virtual void start() { std::cout << "From A\n"; }
};
class B {
public:
void start() { std::cout << "From B\n"; }
};
class C: public A, private B {
};
int main(){
A* a = new C();
a->start(); //Ok, calls A::start()
C* c = new C();
c->start(); //Error, ambiguous
}
To fix that, you will have to use the qualified name such as:
C* c = new C();
c->A::start(); //Ok, calls A::start()
Now, doing a using B::start()
in class C
simply declares the start()
to refer to B::start()
whenever such name is used from an object of C
class A {
public:
virtual void start() { std::cout << "From A\n"; }
};
class B {
public:
void start() { std::cout << "From B\n"; }
};
class C: public A, private B {
public:
using B::start();
};
int main(){
A* a = new C();
a->start(); //Ok, calls A::start()
C* c = new C();
c->start(); //Ok, calls B::start()
}
using B::start
makes the function void B::start()
visible in C
, it does not override it. To call make all the above unqualified member function call, to call B::start()
, you should override the member function in C
, and make it call B::start()
class A {
public:
virtual void start() { std::cout << "From A\n"; }
};
class B {
public:
void start() { std::cout << "From B\n"; }
};
class C: public A, private B {
public:
void start() override { B::start(); }
};
int main(){
A* a = new C();
a->start(); //Ok, calls C::start() which in turn calls B::start()
// ^^^^^^^^^^^^^^^^ - by virtual dispatch
C* c = new C();
c->start(); //Ok, calls C::start() which in turn calls B::start()
}
Related Topics
Best Introduction to C++ Template Metaprogramming
Garbage Collection in C++ -- Why
Video Stabilization with Opencv
Detect Windows or Linux in C, C++
Is a '=Default' Move Constructor Equivalent to a Member-Wise Move Constructor
When to Make a Type Non-Movable in C++11
When to Use Shared_Ptr and When to Use Raw Pointers
Recommended Values for Opencv Detectmultiscale() Parameters
Is There a Name for This Tuple-Creation Idiom
Why Is Cuda Pinned Memory So Fast
How to Find the Intersection of Two Stl Sets
Use Std::Fill to Populate Vector with Increasing Numbers
Matlab VS C++ VS Opencv - Imresize
Using the Less Than Comparison Operator for Strings
How to Do the Equivalent of Memset(This, ...) Without Clobbering the Vtbl