C++ 策略继承和不可访问的受保护成员
模板策略类的受保护成员似乎是不可访问的,即使类层次结构看起来是正确的 例如,使用以下代码段:C++ 策略继承和不可访问的受保护成员,c++,inheritance,visibility,protected,C++,Inheritance,Visibility,Protected,模板策略类的受保护成员似乎是不可访问的,即使类层次结构看起来是正确的 例如,使用以下代码段: #include <iostream> using namespace std; template <class T> class A { protected: T value; T getValue() { return value; } public: A(T value) { this->value = value; } }; te
#include <iostream>
using namespace std;
template <class T>
class A {
protected:
T value;
T getValue() { return value; }
public:
A(T value) { this->value = value; }
};
template <class T, template <class U> class A>
class B : protected A<T> {
public:
B() : A<T>(0) { /* Fake value */ }
void print(A<T>& input) {
cout << input.getValue() << endl;
}
};
int main(int argc, char *argv[]) {
B<int, A> b;
A<int> a(42);
b.print(a);
}
#包括
使用名称空间std;
模板
甲级{
受保护的:
T值;
T getValue(){返回值;}
公众:
(T值){this->value=value;}
};
模板
B类:受保护的A类{
公众:
B():A(0){/*假值*/}
无效打印(A和输入){
cout不要被模板分散注意力。这与错误无关。编译器抱怨的main
中的行创建了B
类型的对象,并尝试访问受保护的成员。这是不合法的,无论类型如何。您只能在成员f内部使用受保护的成员函数或朋友函数。例如:
struct S {
protected:
void f();
};
int main() {
S s;
s.f(); // error: attempts to call a protected member function
}
您误解了受保护访问的含义
受保护的成员可由派生类调用,但只能在类本身包含的基对象上调用
例如,如果我使用以下方法简化问题:
class A {
protected:
void getValue(){}
};
class B : protected A
{
public:
void print(A& input)
{
input.getValue(); //Invallid
}
};
不能对类本身内部的“a”对象以外的“a”对象调用getValue。
这个例子是有效的
void print()
{
getValue(); //Valid, calling the base class getValue()
}
正如Dan Nissenbaum和shakurov所指出的,这也是正确的:
void print(B& input)
{
input.getValue();
}
这是因为我们明确地说输入是B的一个对象。编译器知道B的所有对象都具有对getValue的保护访问权限。在我们传递a&的情况下,该对象也可能是C的一种类型,它可以从具有私有访问权限的a中派生出来。让我们暂时忘记模板,看看这个:
class A {
protected:
int value;
int getValue() { return value; }
public:
A(int value) { this->value = value; }
};
class B : protected A {
public:
B() : A(0) { /* Fake value */ }
void print(A& input) {
cout << input.getValue() << endl;
}
};
A类{
受保护的:
int值;
int getValue(){return value;}
公众:
(int值){this->value=value;}
};
B类:受保护的A类{
公众:
B():A(0){/*假值*/}
无效打印(A和输入){
cout只要作为参数传递的对象的显式声明类是派生类(或进一步的派生类)的类,派生类的成员函数就可以访问作为参数传递的其类型的任何对象中受保护的基类成员
作为基类类型显式传递的对象不能在派生类的成员函数中访问其受保护的成员
换句话说,如果我们有:
class A
{
protected:
int x;
}
class B : public A
{
void foo(B b)
{
b.x; // allowed because 'b' is explicitly declared as an object of class B
}
void goo(A a)
{
a.x; // error because 'a' is explicitly declared as having *base* class type
}
};
…则不允许使用a.x
行,因为参数的显式类型为a
,但受保护访问规则仅适用于显式定义为与尝试访问成员的类相同的类的对象。(…或从其派生的类;即,如果class C
派生自B
,则传递显式声明为classC
对象的对象也将在B
成员函数中具有x
可访问性。)
这是沙库洛夫在写作(意译)时给出的原因
A输入可能不是对B实例的引用(&I)。它可能是A
对另一个子类的引用(该子类很可能具有getValue()
无法访问)
这里也给出了对这个答案的极好解释:
作为一个有趣的问题,我相信这是来自C++标准的:
11.4受保护成员访问[class.Protected]1当
非静态数据成员或非静态成员函数是受保护的
其命名类(11.2)115的成员,如前所述,访问
已授予受保护成员,因为引用发生在朋友中
或某个类C的成员。如果访问要形成指向
成员(5.3.1),嵌套名称说明符应表示C或类
派生自C。所有其他访问都涉及a(可能是隐式的)
对象表达式(5.2.5)。在本例中,对象的类
表达式应为C或从C派生的类
但是,通过将签名更改为void print(B&input)
(即,传递B
对象,而不是a
对象),这是合法的。(注意:假设getValue()
返回有效的内容,而不是void
)。因为封装是基于类的,而不是基于对象的。否则编写赋值运算符将是一场噩梦。我认为答案是@shakurov在编写a时给出的&输入可能不是对B实例的引用。它可能是对另一个子类的引用(可能有getValue()无法访问。因此,只有实际的显式B
类对象才允许访问其受保护的成员。我认为,如果删除“但仅限于类本身包含的基本对象”这句话,这个答案在技术上是正确的。“您不能访问B内部的非公共成员。”是的,你可以。只要B是从A派生的,你就可以访问A类的受保护成员,但只能访问A类内的受保护成员。@DavidJensen我相信你所说的是shakurov在B类内的意思(即,对应于你提到的“仅在A类内”).是的,但他还是说它们不能访问,我是说可以访问。@DavidJensen我读这句话的意思是“你不能访问B中显式A对象的非公开成员”-你是这样读的吗?请参阅,以获取此问题的答案。
class A
{
protected:
int x;
}
class B : public A
{
void foo(B b)
{
b.x; // allowed because 'b' is explicitly declared as an object of class B
}
void goo(A a)
{
a.x; // error because 'a' is explicitly declared as having *base* class type
}
};