Let's say I live in a very quiet apartment with a very mean landlord. If I try to call makeNoise from dogObj with the statement: dogObj->makeNoise(); the compiler informs me I cannot access the private member. That's what I wanted since I only want Fido to bark in emergencies.
But isn't it a bit of a contradiction that I can then call that exact same function from an Animal pointer at runtime? I know polymorphism is not resolved until run-time, so I would not expect a compiler error. But isn't the polymorphic call violating or getting around the "privacy" of Dog::makeNoise() at runtime?
It seems polymorphism offers a way to get around encapsulation?
I know polymorphism is not resolved until run-time, so I would not expect a compiler error. But isn't the polymorphic call violating or getting around the "privacy" of Dog::makeNoise() at runtime?
The first sentence here sums it up. Polymorphism is determined at runtime -- whereas public/private/protected are nonexistent during runtime (it's just something to make compiling nicer). So the privacy of Dog::makeNoise doesn't exist when you run the program, it only matters when you try to compile it.
Your example demonstrates a poor use of public/private qualifiers. Or at least it tries to get them to do something they're not designed to do.
So yes -- you can break encapsulation this way, but only if the encapsulation was poorly designed. Members that are public in the parent class should be public in child classes. To "solve" this, you could change the design slightly:
1 2 3 4 5 6 7 8 9 10 11 12
class Dog : public Animal
{
private:
void reallyMakeNoise()
{
cout << "Bark!\n";
}
public:
void senseDanger() { reallymakeNoise(); }
void makeNoise() { /* do nothing */ }
};
Since you are using a pointer to Animal ( which is allowed to make noise ) and that pointer doesn't know to be a Dog ( which is not allowed ). So it makes noise
Or just make the makeNoise() method protected in the base class.
You can still make it public in Goldfish (and because it's a virtual, that's not a totally evil thing to do, but it isn't exactly a good thing to do either).
Your Dog wrapper will work as intended, and there will be no way to call makeNoise() directly from Animal or Dog, or any class where it doesn't get declared public.
11.6 Access to virtual functions
The access rules (clause 11) for a virtual function are determined by its declaration AND are NOT affected by
the rules for a function that later overrides it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
class B {
public:
virtualint f();
};
class D : public B {
private:
int f();
};
void f()
{
D d;
B* pb = &d;
D* pd = &d;
pb->f(); //OK: B::f() is public,
// D::f() is invoked
pd->f(); //error: D::f() is private
}
Access is checked at the call point using the type of the expression used to denote the
object for which the member function is called (B* in the example above). The access of the member
function in the class in which it was defined (D in the example above) is in general not known.
Thanks very much Disch, Bazzy, jRaskell, and guestGulkan... for all of your comments. I am all ears, I am learning so much from this forum! Thanks again.