Module-level accessibility

Steven Schveighoffer schveiguy at yahoo.com
Tue Oct 5 09:42:14 PDT 2010


On Tue, 05 Oct 2010 12:07:21 -0400, Andrei Alexandrescu  
<SeeWebsiteForEmail at erdani.org> wrote:

> On 10/5/10 7:40 CDT, Steven Schveighoffer wrote:
>> On Tue, 05 Oct 2010 03:25:03 -0400, Jacob Carlborg <doob at me.com> wrote:
>>
>>> Who says we need to implement it as g++ does? DMD could implement it
>>> to not allow that.
>>
>>
>> The derived class controls everything. You cannot take control away from
>> the derived class, it just can't be done:
>>
>> class A
>> {
>> private void cantCallMe();
>> }
>>
>> class B : A
>> {
>> override private void cantCallMe() { butICanCallYou(); }
>> private void butICanCallYou() { /* do impl */ }
>> }
>>
>> so far, private virtual functions == annoyance. I haven't seen anything
>> to prove otherwise yet.
>>
>> -Steve
>
> Private overridable functions come with a guarantee that hooks can never  
> be called from an unknown/uncontrolled context.

And when would a protected hook be callable from an unknown/uncontrolled  
context?  In whose eyes, the base or derived class?

The thing is, any class that defines a private function implementation is  
able to call it.  You can't prevent that.  So in the eyes of any base  
class, it has no way to restrict that.  In the eyes of the derived class,  
it has no control over whether a base class can call it or not.   
Essentially a private overridable function has one additional guarantee  
over a protected function -- a derived class cannot call it if it hasn't  
defined its own version (and isn't in the same module).  But that's not  
the property desired, the property desired for NVI is that *nobody* can  
call it except the base class, even derived classes.  That's simply not  
possible.  So we have to pretend that we can't call it, which is fine,  
just as easy with protected.

Consider this:

abstract class A
{
   public void myAPIFn() { foo(); }
   abstract private void foo();
}

abstract class B : A
{
   public void bar() {myAPIFn();}
}

class C : B
{
   private void foo() {}
}

So good so far.  But I can alter B so it can circumvent myAPIFn by adding  
its own implementation for foo (which does nothing), and now I'm able to  
call foo directly from myAPIFn.  Basically, even though B does not intend  
for its version of foo to be the real implementation (knowing that it's  
abstract and must be derived), it can hijack A's ability to funnel all  
calls through myAPIFn by simply adding a blank function.

I just don't see the benefit over protected anywhere.  You can't say  
"you're allowed to define this, but you're not allowed to call it."  That  
makes no sense at all, and as I've shown, is impossible to enforce.

-Steve


More information about the Digitalmars-d mailing list