Call private method from the world in c++

2019-08-15 02:18发布

问题:

I realy don't understand why it works, but below code shows an example of calling private method from the world without any friend classes:

class A
{
public:
    virtual void someMethod(){
        std::cout << "A::someMethod";
    }
};
class B : public A
{
private:
    virtual void someMethod(){
        std::cout << "B::someMethod";
    }
};
int main(){
    A* a = new B;
    a->someMethod();
}

outputs:

B::someMethod

Doesn't it violates encapsulation rules in C++? For me this is insane. The inheritance is public, but the access modifier in derived class is changed to private, thus the someMethod() in class B is private. So in fact, doing a->someMethod(), we are directly calling a private method from the world.

回答1:

Consider the following code, a modification of the code in the original question:

class A
{
public:
    virtual void X(){
        std::cout << "A::someMethod";
    }
};
class B : public A
{
private:
    virtual void Y(){
        std::cout << "B::someMethod";
    }
};
int main(){
    A* a = new B;
    a->X();
}

It's easy to understand that it's legit to call X(). B inherits it from A as a public member. Theoretically if X() would call Y() this would be legit as well of course, although this is not possible because X() is declared in A which doesn't know Y(). But actually this is the case if X = Y, i.e. if both methods have the same name.

You may think of it as "B inherits a public method from A, that calls a private method (of B) with the same name".



回答2:

a is a pointer to an A object where the method is public, so this is not a violation. Since, you have used virtual, the VTABLE is taken into account and you get the output as B::someMethod.



回答3:

For me this is quite simple. As you are allowed to asign any dynamic type inheritance of a class to an object of the parrent-class it would be vise versa.But as you are assigning the Child to its parrent object pointer this could be bad as if the class of B where bigger as A.

If a program attempts to access the stored value of an object through an lvalue of other than one of the following types the behavior is undefined:

  • the dynamic type of the object,
  • a cv-qualified version of the dynamic type of the object,
  • a type that is the signed or unsigned type corresponding to the dynamic type of the object,
  • a type that is the signed or unsigned type corresponding to a cv-qualified version of the dynamic type of the object,
  • an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union),
  • a type that is a (possibly cv-qualified) base class type of the dynamic type of the object,
  • a char or unsigned char type.

So as in your code you aren't fitting this conditions, but as B has the sime size as A, it would be possible to run it. But it is undefined behavior. And as you creat a new B the memory block a is pointing to has the method of printing B and not A.



回答4:

The access control specifiers (public, protected and private) do not apply to member functions or data members. They apply to member function and data member names. And that works. If you can refer to the member without the name, you can access it.

That's precisely what's happening here. You're calling B::someMethod, but you're calling it using the name A::someMethod, which is public. No problem.

You're suggesting a private member function shouldn't be callable from outside of the class (disregarding friends for the moment). How would your desired semantics work in the following case?

Shared library hider:

hider.h:

typedef void (*FuncType)();

class Hider {
private:
  static void privFunc();

public:
  static void pubFunc();

  FuncType getFunction() const;
};

hider.cpp

#include <cstdlib>
#include <iostream>

#include "hider.h"

void Hider::privFunc() {
  std::cout << "Private\n";
}

void Hider::pubFunc() {
  std::cout << "Public\n";
}

FuncType Hider::getFunction() const {
  if (std::rand() % 2) {
    return &pubFunc;
  } else {
    return &privFunc;
  }
}

Application using library hider

#include "hider.hpp"

int main()
{
  Hider h;
  FuncType f = h.getFunc();
  f();
}

What about the call to f()? Should it fail at runtime 50% of the time with some form of access control violation?


As suggested by DyP in the comments, a more realistic scenario is the well-known "template method" design pattern:

class Container
{
public:
  void insert(const Item &item) {
    preInsert();
    data.insert(item);
    postInsert();
  }

private:
  std::vector<Item> data;

  virtual void preInsert() = 0;
  virtual void postInsert() = 0;
};


class ThreadSafeContainer : public Container
{
private:
  std::mutex m;

  virtual void preInsert() {
    m.lock();
  }

  virtual void postInsert() {
    m.unlock();
  }
};

Your semantics would mean this code wouldn't compile.