Тонкая ошибка наследования С++ с защищенными полями
Ниже представлен тонкий пример доступа к защищенному экземпляром поле x.
B является подклассом A, поэтому любая переменная типа B также имеет тип A.
Почему B:: foo() имеет доступ к полю b x, но не к полю x?
class A {
protected:
int x;
};
class B : public A {
protected:
A *a;
B *b;
public:
void foo() {
int u = x; // OK : accessing inherited protected field x
int v = b->x; // OK : accessing b protected field x
int w = a->x; // ERROR : accessing a protected field x
}
};
Вот ошибка, которую я получаю с g++
$ g++ -c A.cpp
A.cpp: In member function ‘void B::foo()’:
A.cpp:3: error: ‘int A::x’ is protected
A.cpp:14: error: within this context
Ответы
Ответ 1
Так как B
является публично унаследованным от A
, защищенный член становится B-защищенным членом (-ами), поэтому B может обращаться к своим защищенным членам, как обычно, из его функций-членов. То есть объекты B
могут получить доступ к защищенным членам B
из своих функций-членов.
Но защищенные члены не могут быть доступны за пределами класса, используя объект типа A
.
Вот соответствующий текст из Стандарта (2003)
11.5 Защищенный доступ к элементу [class.protected]
Когда функция друга или члена производного класса ссылается на защищенную нестатистическую функцию-член или защищенный нестатический член данных базового класса, проверка доступа применяется в дополнение к тем, которые описаны ранее в разделе 11.102). За исключением случаев, когда формируется указатель на член (5.3.1), , доступ должен быть через указатель на, ссылку или объект самого производного класса (или любого класса, полученного из этого класса) (5.2.5). Если доступ заключается в формировании указателя на член, спецификатор вложенного имени должен указывать производный класс (или любой класс, полученный из этого класса).
И пример следует из самого Стандарта (2003):
[Example:
class B {
protected:
int i;
static int j;
};
class D1 : public B {
};
class D2 : public B {
friend void fr(B*,D1*,D2*);
void mem(B*,D1*);
};
void fr(B* pb, D1* p1, D2* p2)
{
pb->i = 1; // ill-formed
p1->i = 2; // ill-formed
p2->i = 3; // OK (access through a D2)
p2->B::i = 4; // OK (access through a D2, even though naming class is B)
int B::* pmi_B = &B::i; // ill-formed
int B::* pmi_B2 = &D2::i; // OK (type of &D2::i is int B::*)
B::j = 5; // OK (because refers to static member)
D2::j =6; // OK (because refers to static member)
}
void D2::mem(B* pb, D1* p1)
{
pb->i = 1; // ill-formed
p1->i = 2; // ill-formed
i = 3; // OK (access through this)
B::i = 4; // OK (access through this, qualification ignored)
int B::* pmi_B = &B::i; // ill-formed
int B::* pmi_B2 = &D2::i; // OK
j = 5; // OK (because j refers to static member)
B::j = 6; // OK (because B::j refers to static member)
}
void g(B* pb, D1* p1, D2* p2)
{
pb->i = 1; // ill-formed
p1->i = 2; // ill-formed
p2->i = 3; // ill-formed
}
—end example]
Примечание в приведенном выше примере fr()
является дружной функцией D2
, mem()
является функцией-членом D2
, а g()
не является ни другом, ни функцией-членом.
Ответ 2
Рассмотрим:
class A {
protected:
int x;
};
class C : public A
{
};
class B : public A {
protected:
unique_ptr<A> a;
public:
B() : a(new C) // a now points to an instance of "C"
{ }
void foo() {
int w = a->x; // B accessing a protected member of a C? Oops.
}
};
Ответ 3
В Наследование на общественных началах:
Все Public members
базового класса становятся Public members
производного класса &
Все Protected members
базового класса становятся Protected members
Derived Class
.
В соответствии с приведенным выше правилом:
защищенный член x
от A
становится защищенным членом класса B
.
class B
может получить доступ к своим защищенным членам в своей функции-члене foo
, но он может обращаться только к членам A
, через которые он был получен не всеми классами A
.
В этом случае class B
содержит указатель A
A
, он не может получить доступ к защищенным членам этого содержащегося класса.
Почему B::foo()
может получить доступ к элементам содержащегося указателя class B
B
?
Правило:
В С++ управление доступом работает только для каждого класса, а не для каждого объекта.
Таким образом, экземпляр class B
всегда будет иметь доступ ко всем членам другого экземпляра class B
.
Пример кода, который демонстрирует правило:
#include<iostream>
class MyClass
{
public:
MyClass (const std::string& data) : mData(data)
{
}
const std::string& getData(const MyClass &instance) const
{
return instance.mData;
}
private:
std::string mData;
};
int main() {
MyClass a("Stack");
MyClass b("Overflow");
std::cout << "b via a = " << a.getData(b) << std::endl;
return 0;
}
Ответ 4
Почему B:: foo() получает доступ к полю b x, но не к полю x?
Защищенный член может быть доступен только другим членам того же класса (или производных классов).
b->x
указывает на защищенный член экземпляра класса B (через наследование), поэтому B::foo()
может получить к нему доступ.
a->x
указывает на защищенный член экземпляра класса A, поэтому B::foo()
не может получить к нему доступ.
Ответ 5
Класс B
не идентичен классу A
. Поэтому члены класса B
не могут получить доступ к непубличным членам класса A
.
С другой стороны, класс B
выводится публично из класса A
, поэтому класс B
теперь имеет (защищенный) член x
, к которому может получить доступ любой член класса B
.
Ответ 6
Давайте начнем с базовой концепции,
class A {
protected:
int x;
};
class B : public A {
public:
void foo() {
int u = x; // OK : accessing inherited protected field
}
};
Поскольку дочерний элемент наследует родителя, дочерний элемент получает x. Следовательно, вы можете получить доступ к x непосредственно в foo() методе child. Это концепция защищенных переменных. Вы можете напрямую обращаться к защищенным переменным родительского элемента.
Примечание. Здесь я говорю, что вы можете получить доступ к x напрямую, но не через объект A! Какая разница? Поскольку x защищен, вы не можете получить доступ к защищенным объектам вне A. Не имеет значения, где он находится - если его основной или дочерний. Вот почему вы не можете получить доступ следующим образом
class B : public A {
protected:
A *a;
public:
void foo() {
int u = x; // OK : accessing inherited protected field x
int w = a->x; // ERROR : accessing a protected field x
}
};
Вот интересная концепция. Вы можете получить доступ к частной переменной класса, используя свой объект в классе!
class dummy {
private :
int x;
public:
void foo() {
dummy *d;
int y = d->x; // Even though x is private, still you can access x from object of d - But only with in this class. You cannot do the same outside the class.
}
};
//То же самое для защищенных переменных. Если вы можете получить доступ к следующему примеру.
class B : public A {
protected:
A *a;
B *b;
public:
void foo() {
int u = x; // OK : accessing inherited protected field x
int y = b->x; // OK : accessing b protected field x
int w = a->x; // ERROR : accessing a protected field x
}
};
Надеюсь, он объяснит:)
С++ - это полное объектно-ориентированное программирование, где, поскольку Java является чисто объектно-ориентированным:)