Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does C++ allow access restrictions on public inherited base methods?

Regarding the question "How to publicly inherit from a base class but make some of public methods from the base class private in the derived class?", I have a follow-up question:

I can understand that the C++ standard allows a derived class to relax access restrictions of an inherited method, but I can not think of any legitimate use case where it would make sense to impose access restrictions in the derived class.

From my understanding of the concept of inheritance, if class Derived is public class Base, then anything you can do with Base can also be done with Derived. If one does not want Derived to fulfill the interface of Base, one should not use (public) inheritance in the first place. (Indeed, when I encountered this technique in the wild in ROOT's TH2::Fill(double), is was a clear case of inheritance abuse.)

For virtual methods, access restrictions in Derived are also useless, because any user of Derived can use them by casting a Derived* into a Base*.

So, from my limited C++ newbie point of view, these restrictions are misleading (the programmer of Derived might assume that his virtual now-protected method is not called by anyone else, when in fact it might be) and also confuses [me with regard to] what public inheritance should imply.

Is there some legitimate use case I am missing?

like image 401
user2247306 Avatar asked Nov 03 '16 16:11

user2247306


People also ask

Why do we use protected specifier for base class data members in inheritance?

The protected access specifier allows the class the member belongs to, friends, and derived classes to access the member. However, protected members are not accessible from outside the class.

When the access specifier for an inherited base class is public?

If the derived class is declared with the keyword class , the default access specifier in its base list specifiers is private . If the derived class is declared with the keyword struct , the default access specifier in its base list specifiers is public .

What happens if access mode is not specified on inheriting a class?

Explanation: If the access mode is not specified during inheritance, the class is inherited privately by default. This is to ensure the security of data and to maintain OOP features. Hence it is not mandatory to specify the access mode if we want the class to be inherited privately.

Can private members access inherited classes?

A base class's private members are never accessible directly from a derived class, but can be accessed through calls to the public and protected members of the base class.


1 Answers

From Herb Sutter, Guru of the Week #18:

Guideline #3: Only if derived classes need to invoke the base implementation of a virtual function, make the virtual function protected.

For detail answer, please read my comment in the code written below:

#include <iostream>
#include <typeinfo>
#include <memory>

struct Ultimate_base {
    virtual ~Ultimate_base() {}
    void foo() { do_foo(); }
protected:
    // Make this method protected, so the derived class of this class
    // can invoke this implementation
    virtual void do_foo() { std::cout << "Ultimate_base::foo"; }
};

struct Our_derived : Ultimate_base {
private:
    // Make this method private, so the derived class of this class
    // can't  invoke this implementation
    void do_foo() {
        Ultimate_base::do_foo();
        std::cout << " Our_derived::foo";
    }
};

struct Derive_from_derive : Our_derived {
private:
    void do_foo() {
        // You can't call below code
        // vvvvvvvvvvvvvvvvvvvvvv
        // Our_derived::do_foo();
        std::cout << " Derive_from_derive::foo";
    }
};

// This class is marked final by making its destructor private
// of course, from C++11, you have new keyword final
struct Derive_with_private_dtor : Ultimate_base {
private:
    ~Derive_with_private_dtor() {}
};

// You can't have below class because its destructor needs to invoke
// its direct base class destructor
// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
/* 
struct Derive_from_private_dtor : Derive_with_private_dtor {
};
*/

int main() {
    std::unique_ptr<Ultimate_base> p = std::make_unique<Our_derived>();
    p->foo();
    p = std::make_unique<Derive_from_derive>();
    p->foo();
    p.reset(new Derive_with_private_dtor);
}
like image 62
Danh Avatar answered Oct 02 '22 22:10

Danh