Why does C++ allow this pointer polymorphism of private parent?

334 Views Asked by At

If I inherit a derived class privately from a base class, I cannot get the polymorphism of the inhritetted class.

But I can get the polymorphism of 'this' pointer inside the derived class.

I'm very curious about why 'this' pointer is allowed to polymorphism to its private parent, however, a pointer outside of the class is not.

Thanks in advance!

#include <iostream>

class Shape
{
public:
    virtual void say() const = 0;
    virtual void who()
    {
        std::cout << "Shape" << std::endl;
    }
    void whoAmI() {
        this->who();
    }
};

class Squire : private Shape
{
public:
    virtual void say() const
    {
        std::cout << "Squire" << std::endl;
    }
    
    void doSay()
    {
        // why this pointer to Shape type is allowed in class?
        privateSay(this);
    }
    
private:
    void privateSay(Shape* s)
    {
        s->say();
    }
};

void say(Shape* s)
{
    s->say();
}

int main(int argc, const char * argv[]) {
    // insert code here...
    
    Squire* s = new Squire();
    // allowed
    s->doSay();
    
    // not allowd, compile errors
    // Cannot cast 'Squire' to its private base class 'Shape'
    say(s);
    
    return 0;
}

=========

Edit to clarify. Thanks for all the answers. To clarify my question, I think if C++ allows this behavior, mybe it violates the encapsulation a little.

A clearer case:

#include <iostream>

class Base
{
public:
    void baseFunc() const
    {
        std::cout << "baseFunc" << std::endl;
    }
};

class Worker
{
public:
    Worker(Base *pBase)
    {
        base_ = pBase;
    }
    
    void breakFunc() const
    {
        base_->baseFunc();
    }
private:
    Base *base_;
};

class Derived : private Base
{
public:
    void init()
    {
        worker_ = new Worker(this);
    }
    
    Worker* getWorker()
    {
        return worker_;
    }

private:
    Worker *worker_;
};

int main()
{
    Derived derived;
    derived.init();
    
    
    Worker *worker = derived.getWorker();
    worker->breakFunc();
}

The object of Base class only exisits in the object of Derived class as the parent object part, however, the Derived class inheritents Base class privately, which means the object of the Derived class has-an object of the Base class, privately.

The code above violates the encapsulation rule of the Derived class, however, even no warnning is given during compilation.

In my opinion, an explicit cast should be used under the circumstances,or an warning should be given during compilation.

2

There are 2 best solutions below

0
On BEST ANSWER

The question asked here is why a base class is accessible when privately inherited.
The answer is rather simple: Because base classes are always visible to inherited regardless of the type of inheritance.

You can static_cast to a base class when the base is visible within the context.


class base
{
};

class inherited : base
{
public:

void foo()
{
    // works, base is visible within the method.
    auto pBase = static_cast<base*>(this);
}
};

int main() {
    inherited i{};
    // auto pBase = static_cast<base*>(&i); // won't compile, base is not visible
    return 0;
}
1
On

Private inheritance means that both public and protected symbols from the base class become private symbols of the derived class. private symbols of the base class are inaccessible to the derived class. With exception of private virtual methods which can still be overridden.

this pointers, or in general Class* ptr used inside the class, can access private fields of the class.

Since the symbol Shape::say is public in Shape, it becomes private in Squire(BTW Did you mean Square?). This prevents its usage by public but not by this. Due to rules of virtuals, this "privatizes" the whole chain of overridden methods.

void Squire::privateSay(Shape* s)
{
    // Works because `Shape::say` is a public symbol of Shape -> private symbol of Squire and `this` can access private symbols.
    s->say();
}
void say(Shape* s)
{
    // Shape::say is a private symbol, obviously cannot be called.
    s->say();
}

If there existed private Shape::foo() method, it would not be accessible even through this in any of the Squire's methods.