In Java and C++ subclasses introduce the difference between reduced accessibility of superclass function overrides

  • 2020-12-16 05:57:23
  • OfStack

preface

The title "Narrowing the accessibility of subclasses to superclass function overrides in Java and C++" seems academic, but it is an easy problem to ignore. This paper attempts to elaborate on the differences between Java and C++.

Let's start with what is called "subclass accessibility reduction of superclass function overrides." For inheritance, subclasses can override "virtual functions" of the parent class -- although there is no such term in Java, all functions of Java can be considered virtual functions, since all functions of Java can be overridden by subclasses. Here only borrows the "virtual function" the meaning of the 1 noun, not to get into the details of the language. Both Java and C++ allow you to change the accessibility of functions when overridden. Accessibility is defined as the use of public, protected, private and other access controls to control whether a function can be accessed. The usual order of accessibility is (since there is no concept of packages in C++, package access controllers are not considered for the time being, which does not affect the discussion here) :


public > protected > private

Take Java for example:


class Base {
 protected void sayHello() {
  System.out.println("Hello in Base");
 }
}
class Child extends Base {
 public void sayHello() {
  System.out.println("Hello in Child");
 }
}

Note: here sayHello() Function. In the parent class Base, this function is decorated with the protected access control. The subclass replaces it with public, which doesn't have any problems. When a subclass overrides a superclass function, extending accessibility is usually not a problem.

Java and C++ take different strategies when subclasses reduce the accessibility of superclass function overrides.

First take Java as an example and look at the following code:


class Base {
 public void sayHello() {
  System.out.println("Hello in Base");
 }
}
class Child extends Base {
 private void sayHello() {
  System.out.println("Hello in Child");
 }
}

In the above code, the highlighted line 8 will have a compile error - this code will not compile at all! Java does not allow subclasses to reduce accessibility when overwriting superclass functions. As for the reason, we can use an example to illustrate. For example, we write the following code outside the class:


Base base = new Base();
base.sayHello();
base = new Child();
base.sayHello();

If the previous code can be compiled, then one possibility exists: when base points to new Base(), sayHello() is accessible, but when base points to new Child(), sayHello() is not! From Java's point of view this is a contradiction that should be avoided, so Java says from a compiler's point of view that we cannot write the above code.

For C++, the situation is different again. Take an example of C++ :


class Base {
public:
  virtual void sayHello() {
    std::cout << "Hello in Base";
  }
}
class Child : public Base {
private:
  void sayHello() {
    std::cout << "Hello in Child";
  }
}

This code is completely correct in C++. Note that the subclasses here reduce accessibility by overwriting the superclass functions. If you don't see a problem, we can write the following code outside the class:


Child child;
child.sayHello(); //  It doesn't compile because  sayHello()  is  private  the 
static_cast<Base&>(child).sayHello(); //  It can be compiled because  sayHello()  is  public  the 

The line 2 call fails because in Child, sayHello() It is private and cannot be called externally. However, things change when we cast Child to an Base object using static_cast -- for Base, sayHello() It's public, so it can be called normally.

To this point, the following examples can be found in section 1 of Access to virtual functions 1 of C++ standard Member access control 1:


class B {
public:
  virtual int 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
}

The explanation given by C++ standard is as follows:

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.

There are two main points in simple translation:

Access control is checked at call time, that is, whoever calls the function is checked for access to the function Accessibility 1 of member functions in a class is generally unknown, that is, when you check accessibility, you do not know whether the function was public or private at the time of definition, so you cannot check accessibility accordingly

Because of this, it seems that the caller of C++ can "smartly" call functions that would otherwise be inaccessible through some sort of technical transformation. A more practical example is: in Qt, QObject::event() The function is public and the subclass QWidget event() The function is protected. Read the Qt code for details.

In summary, Java strictly limits the ability of subclasses to reduce function accessibility when they override superclass functions, but C++ does not. Personally, I think that from the perspective of software engineering, the provisions of Java are undoubtedly more significant in engineering and the function calls are also more 1. The STANDARD of C++ will significantly simplify compiler implementation, but is not a good reference for engineering purposes.

PS: The official version of the C++ standard is available for purchase, but the draft is free to download. C + + draft standard download address can be found in the following pages: https: / / isocpp org/std/the - standard

conclusion


Related articles: