Может ли класс класса класса получить доступ к базовым классам базовых классов на объект производного класса?
Я удивлен, что код ниже компилируется.
Кажется, что класс, подружившийся с (наследуемым) базовым классом, может получить доступ к члену базового класса, предоставленному экземпляром производного класса.
Если наследование изменено на 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
. - конец пример]
И
- Если наследование изменено на
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.
Для справки: