简体   繁体   English

在C ++继承中访问成员函数

[英]Access of member functions in C++ inheritance

I am just confused about the tiny program on inheritance below: 我对以下关于继承的小程序感到困惑:

#include<iostream>
using namespace std;

struct B {
    virtual int f() { return 1; }
}; // f is public in B

class D : public B { 
    int f() { return 2; }
}; // f is private in D

int main()
{
    D d;
    B& b = d;
    cout<<b.f()<<endl; // OK: B::f() is public, D::f() is invoked even though it's private
    cout<<d.f()<<endl; // error: D::f() is private
}
  1. I can't figure out why D::f() is private, D is public inherited from B , so the public function f in B 我无法弄清楚为什么D::f()是私有的, D公共继承B ,所以公共函数fB
    is also public in D (I know without inheritance, member access is private by default) D也是公共的(我知道没有继承,默认情况下成员访问是私有的)
  2. f is a virtual function in B , so if we call bf() , we actually call D::f() , but just as the illustration mentioned, why D::f() is able to be invoked even though it's private? fB的虚函数,所以如果我们调用bf() ,我们实际调用D::f() ,但正如图中所提到的,为什么D::f()能够被调用,即使它是私有的?

Can anyone explain the simple inheritance problem in detail? 任何人都可以详细解释简单的继承问题吗?

This has to do that with virtual dispatch is a runtime concept. 这必须做到这一点,虚拟调度是一个运行时概念。 The class B doesn't care which class extends it, and it doesn't care if it's private or public because it can't know. B类并不关心哪个类扩展它,并且它不关心它是私有的还是公共的,因为它无法知道。

I can't figure out why D::f() is private, D is public inherited from B, so the public function f in B is also public in D(I know without inheritance, member access is private by default) 我无法弄清楚为什么D :: f()是私有的,D是公共继承自B,所以B中的公共函数f在D中也是公共的(我知道没有继承,默认情况下成员访问是私有的)

D::f() is private because you made it private. D::f()是私有的,因为你把它变成了私有的。 This rule isn't affect by inheritance nor virtual dispatch. 此规则不受继承或虚拟分派的影响。

f is a virtual function in B, so if we call bf(), we actually call D::f(), but just as the illustration mentioned, why D::f() is able to be invoked even though it's private? f是B中的虚函数,所以如果我们调用bf(),我们实际调用D :: f(),但正如图中所提到的,为什么D :: f()能够被调用,即使它是私有的?

Because in reality, when invoking bf() , the compiler has no idea which function will actually be called. 因为实际上,在调用bf() ,编译器不知道实际调用哪个函数。 It will simply call the function f() , and since B::f is virtual, the called function will be chosen at runtime . 它将简单地调用函数f() ,并且由于B::f是虚拟的,因此将在运行时选择被调用的函数。 The runtime program has no information about which function is private or protected. 运行时程序没有关于哪个功能是私有还是受保护的信息。 It only know functions. 它只知道功能。

If the function is chosen at runtime, the compiler can't know at compile-time what function will be called, and the access specifier can't be known. 如果在运行时选择了该函数,则编译器无法在编译时知道将调用哪个函数,并且无法知道访问说明符。 In fact, the compiler won't even try to check if the function called will be private or not. 实际上,编译器甚至不会尝试检查被调用的函数是否是私有的。 The access specifier could be in some code that the compiler haven't seen yet. 访问说明符可能在编译器还没有看到的某些代码中。

As you've experienced, you can't call D::f directly. 正如您所经历的那样,您无法直接调用D::f This is exactly what private will do: prohibit direct access of the member. 这正是私人所做的:禁止直接访问会员。 You can, however, access it indirectly through a pointer or a reference. 但是,您可以通过指针或引用间接访问它。 The virtual dispatch you use will do that internally. 您使用的虚拟调度将在内部执行。

Access specifiers apply to the function name only, they aren't some restriction on how or when the function can be called by other means. 访问说明符仅适用于函数名称 ,它们对通过其他方式调用函数的方式或时间没有一些限制。 A private function could be called outside the class if it is made available by some means other than its name (for example, a function pointer). 如果通过除名称之外的某些方式(例如,函数指针)使私有函数可用,则可以在类外部调用私函数。

For a class declared with class keyword, the default access-specifier is private . 对于使用class关键字声明的class ,默认的访问说明符是private Your code is the same as: 您的代码与以下内容相同:

// ...
class D: public B
{
private:
  int f() { return 2; }
};

As you can see, f is private in D . 如你所见, fD是私有的。 It makes no difference what the access specifier was of any function in B with the same name. 对于具有相同名称的B的任何函数,访问说明符是什么没有区别。 Be clear in your mind that B::f() and D::f() are two different functions. 请记住, B::f()D::f()是两个不同的函数。

The effect of the virtual keyword is that if f() without a scope qualifier is called on a B reference that refers to a D object, then even though it resolves to B::f() , actually D::f() is invoked instead. virtual关键字的作用是如果在引用D对象的B引用上调用没有范围限定符的f() ,那么即使它解析为B::f() ,实际上D::f()也是相反调用。

This process still uses the access specifier for B::f() : access is checked at compile-time; 这个过程仍然使用B::f()的访问说明符:在编译时检查访问; but it might be run-time matter as to which function is called. 但是关于调用哪个函数可能是运行时间问题。

The C++ Standard has an exact example of this: C ++标准有一个确切的例子:

11.5 Access to virtual functions [class.access.virt] 11.5访问虚函数[class.access.virt]

1 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虚函数的访问规则(第11条)由其声明确定,不受稍后覆盖它的函数规则的影响。 [ Example: [ 例如:

 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 } 

-- end example ] - 结束例子 ]

Can't explain it any clearer. 无法解释清楚。

The answer given illustrates what is being done, but why would you ever want to do this, where the base class calls private virtual functions? 给出的答案说明了正在做什么,但为什么你会想要这样做,基类调用private虚函数?

Well, there is a design pattern called the template method pattern that uses this technique of having a base class that calls private virtual functions in the derived class. 好吧,有一种称为模板方法模式的设计模式,它使用这种技术,它具有一个在派生类中调用私有虚函数的基类。

struct B 
{
    virtual ~B() {};
    int do_some_algorithm()
    {
       do_step_1();
       do_step_2();
       do_step_3();
    }

    private:
          virtual void do_step_1() {}
          virtual void do_step_2() {}
          virtual void do_step_3() {}
};

class D : public B 
{ 
   void do_step_1() 
   {
      // custom implementation
   }
   void do_step_2() 
   {
      // custom implementation
   }

   void do_step_3() 
   {
      // custom implementation
   }
};

int main()
{
   D dInstance;
   B * pB = &dInstance;
   pB->do_some_algorithm();
}

This allows us to not expose the custom steps of class D to the public interface, but at the same time allows B to call these functions using a public function. 这允许我们不将D类的自定义步骤暴露给public接口,但同时允许B使用public函数调用这些函数。

This actually has less to do with virtual dispatch and more to do with what access specifiers mean. 这实际上与虚拟调度关系较少,而与访问说明符的含义有关。

The function itself is not private ; 功能本身不是private ; its name is. 它的名字是。

Consequently, the function cannot be named outside of the scope of the class, eg from main . 因此,函数不能在类的范围之外命名,例如从main However, you can still do so through a name that is public (ie the base's virtual function that is overridden) or from a scope in which the function's name is accessible despite the private qualifier (eg a member function of that class). 但是,您仍然可以通过public名称(即被覆盖的基本虚函数)或尽可能使用private限定符(例如该类的成员函数)访问函数名称的作用域来执行此操作。

That's just how it works. 这就是它的工作原理。

why D::f() is able to be invoked even though it's private? 为什么D :: f()能够被调用,即使它是私有的?

To understand virtual function mechanism it is good to know, how it is usually implemented. 要了解虚函数机制,最好知道它是如何实现的。 A function at runtime is actually no more than an address in memory where the executable code of the function body locates. 运行时的函数实际上只不过是函数体的可执行代码所在的内存中的地址。 To call the function we need to know its address (a pointer). 要调用该函数,我们需要知道它的地址(指针)。 C++ object with virtual functions representation in memory contains so called vtable - an array of pointers to the virtual functions. 内存中具有虚函数表示的C ++对象包含所谓的vtable - 一个指向虚函数的指针数组。

vtable典型的实现

Key point is that in derived classes vtable repeats (and may extend) the vtable of base class, but if the virtual function is overriden its pointer is replaced in derived object's vtable. 关键点是在派生类中,vtable重复(并可能扩展)基类的vtable,但是如果虚函数被覆盖,则其指针将被替换为派生对象的vtable。

When a virtual function call is done through the base class pointer, the address of the virtual function is calculated as an offset in the vtable array. 当通过基类指针完成虚函数调用时,虚函数的地址计算为vtable数组中的偏移量。 No other checks are done, just the function address is taken. 没有进行其他检查,只需要执行功能地址。 If it is a base class object, it will be the address of the base class function. 如果它是基类对象,它将是基类函数的地址。 If it is a derived class object, it will be the address of the derived class function, does not matter if it was declared private or not. 如果它是派生类对象,它将是派生类函数的地址,无论它是否被声明为私有都无关紧要。

This how it works. 这是怎么回事。

Member of struct is default to be public, and member of class is default to be private. struct成员默认为public,而class成员默认为private。 So f() in B is public and when it's derived to D, because you didn't explicitly declare it is public, so according to rules of derivation, it became private. 所以B中的f()是公共的,当它被导出到D时,因为你没有明确地声明它是公共的,所以根据推导规则,它变成了私有的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM