Может ли класс класса класса получить доступ к базовым классам базовых классов на объект производного класса?

Я удивлен, что код ниже компилируется.

Кажется, что класс, подружившийся с (наследуемым) базовым классом, может получить доступ к члену базового класса, предоставленному экземпляром производного класса.

Если наследование изменено на private, компиляция завершится неудачно.

Короче говоря, как d.b_var действует в пределах F::func(D& d)?

#include <iostream>
#include <string>
using namespace std;

class B{
    int b_var;
    friend class F;
};

class D: public B{
    int d_var;
};

class F{
    public:
        void func(D &d){
            d.b_var = 5; 
        }
};

int main()
{
    cout<<"fine";
}

Ответ 1

Объект class D состоит из двух отдельных частей:

part containing members of B 
part containing members of D

Вот почему концепция среза объектов работает, когда мы делаем:

D objD;
B objB = objD;

Теперь мы можем получить доступ изнутри object of class D, part containing members of B через objB. Компилятор запоминает или может различать две части внутри class D. Таким образом, компилятор знает, к чему осуществляется доступ через.

Оператор friend class F; внутри class B просто сообщает, что member functions of class F может обращаться к private, protected and public членам class B. То есть для member functions of class F все члены class B равны public.

Собственно, внутри каждого класса есть три раздела w.r.t доступность:

public
protected
private 

Итак, когда мы объявляем некоторые class B:

class B
{
    public:
        int a;
    protected:
        int b;
    public:
        int c;
};

то следующие 3 секции создаются внутри класса B, как показано выше.

Теперь, когда мы объявляем class F friend:

class B
{
    friend class F;
    private:
        int a;
    protected:
        int b;
    public:
        int c;            
};

тогда компилятор создает разделы следующим образом:

class B
{
    friend class F;
    private:
        int a;
    protected:
        int b;
    public:
        int c;
        //int a;  only for member functions of class F
        //int b;  only for member functions of class F             
};

Обратите внимание, что int a; и int b; теперь доступны для member functions class F.

Теперь, когда class D получается publicly из class B, тогда раздел public class B становится public секцией class D. Similary, раздел protected class B становится секцией protected class D. Следовательно, раздел public раздела class B можно получить через объект class D. А поскольку B::a; и B::b; находятся в общедоступном разделе для members functions of class F, поэтому B::a и B::b можно получить через объект class D. Также обратите внимание, что хотя после деривации int a; и int b; становятся членами class D, все еще компилятор может различать их и считает их part of class B.

Теперь, когда class D получается privately из class B, тогда раздел public class B становится секцией private class D. Similary, раздел protected class B становится защищенной секцией class D. Поэтому теперь часть раздела public внутри class B не может быть доступна через объект class D. Напомним, что в class B, B::a; и B::b; изначально находятся в публичном разделе для members functions of class F, но после private вывода члены class B ie B::a и B::b теперь находятся в частной секции class D. Следовательно, B::a и B::b не могут получать доступ через объект class D. Также обратите внимание, что хотя после деривации int a; и int b; становятся членами class D, все еще компилятор может их отличить и считает их part of class B. После вывода доступность и правила некоторых членов class B изменились.

Поскольку этот вопрос несколько относится к эффекту public, protected and private, поэтому для полноты, пожалуйста, смотрите: Почему производный класс не может получить доступ к защищенному члену своего базового класса с помощью указателя на базу?

Ответ 2

D является B, когда используется публичное наследование. Поэтому доступ к b_var по-прежнему совершенно легален.
Однако вы получите сообщение об ошибке, если попытаетесь получить доступ к d_var, поскольку сама дружба не наследуется, как вам кажется.

Наследование всегда заставляет всех членов базы быть членами производного. Спецификаторы доступа влияют только на видимость идентификатора. Поэтому, если доступ к закрытому члену незаконно создает другую ошибку для доступа к идентификатору, который не существует.

Ответ 3

  • Кажется, что какая-то дружба наследуется, а класс друга может получить доступ к члену производного класса.
    Короче говоря, как d.b_var действует в пределах F::func(D& d)?

d.b_var может вводить в заблуждение. Чтобы быть более точным (другой способ увидеть его), b_var не является (прямым) членом производного класса D. Вместо этого объект D содержит подобъект базового класса B, который имеет член b_var и может получить доступ к другу F. (Так же мы можем написать d.b_var как d.B::b_var.)

$10/3 Производные классы [Class.derived]:

В базовом-спецификаторе-списке указывается тип базового класса подобъектов, содержащихся в объекте типа производного класса. [ Пример:

struct Base {
  int a, b, c;
};

struct Derived : Base {
  int b;
};

struct Derived2 : Derived {
  int c;
};

Здесь объект класса Derived2 будет иметь подобъект класса Derived, который, в свою очередь, будет иметь подобъект класса Base. - конец пример]

И

  1. Если наследование изменено на private, компиляция завершится неудачно.

Поскольку

class B {
    int b_var;
    friend class F;
};

class D: private B {
    int d_var;
};

class F{
public:
    void func(D &d) {
        d.b_var = 5;  // Fail. Can't access subobject of B
        d.d_var = 5;  // Fail. Can't access member of D
    }
};

Тогда

class B {
    int b_var;
};

class D: private B {
    friend class F;
    int d_var;
};

class F{
public:
    void func(D &d) {
        d.b_var = 5;  // Fail. Can't access b_var of subobject of B
        d.d_var = 5;  // Fine.
    }
};

Обратите внимание, что в последнем случае даже F является другом класса D, он может получить доступ ко всем частным/защищенным членам D, но не включает членов в подобъекте B, потому что они не являются (прямых) класса D.

Ответ 4

Пока есть хорошие ответы, я думаю, что некоторые изображения тоже помогут здесь.

B-class

Это абстракция вашего класса B. F имеет доступ ко всем своим членам.

Когда вы создаете экземпляр объекта D, он выглядит как

D-class

Он все еще является объектом B, но также и объектом D. Он расширяет B так сказать. F может все еще получить доступ к части из B, поскольку она все еще существует, но не от D.

Обратите внимание, что эти абстракции на самом деле не отображают макет в памяти и не объясняют переопределение и т.д. Но они предназначены только для этого вопроса.

Ответ 5

Вы пишете:

Кажется, что некоторая дружба наследуется, а класс друга может получить доступ к члену производного класса.

Но это скорее должно быть:

Кажется, что класс, подружившийся с (наследуемым) базовым классом, может получить доступ к частному члену базового класса, предоставленному экземпляром производного класса.

Или:

Кажется, что класс, подружившийся с другим классом, может получить доступ к частным членам своих экземпляров.

Это относится к вашему вопросу:

Короче говоря, как d.b_var действует в пределах F::func(D& d)?

Потому что d.b_var является членом экземпляра класса B (через полиморфизм), к которому имеют доступ экземпляры класса F (через статус друга).

Это не работает с d.d_var, потому что дружба с базовым классом не наследуется, и экземпляры класса F, следовательно, не имеют доступа к закрытым членам d.

Это не работает с частным (или защищенным) наследованием, потому что добавляется еще один "уровень ограничения доступа". Кроме того, вам также необходимо предоставить доступ к частным унаследованным членам производного класса (тогда это d.b_var). Например, сделав также D другом для F.

Для справки: