我有一个基类A和派生类B
B类派生自A作为公共
如果 A是a类是成员变量
,我想访问成员变量的地址当我使用受保护和公共访问说明符时,我正在观察不同的行为。
当A类成员a 受保护时,我得到:
cout<<&A::a << endl;
给我一个编译错误..
但是cout<<&(A::a)<<endl;
是有效的并且给我正确的结果。
当A级成员a 公开时,我得到的是:
为什么会出现这种情况?
以下是完整代码:
#include <iostream>
using namespace std;
class A
{
protected:
int a;
void fun()
{
cout<<"A's fun"<<endl;
}
public:
A(int Aarg):a(Aarg)
{
cout << "A's construction done" <<endl;
}
};
class B: public A
{
protected:
int b;
public:
void fun()
{
cout << "B's fun"<<endl;
}
B(int As, int Bs):A(As), b(Bs)
{
std::cout<<"B's Construction Done" <<std::endl;
}
void show()
{
A::fun();
//cout << a << endl;
cout<<&A::a << endl; //compilation error
}
};
int main()
{
B(10,20).show();
return 0;
}
现在我可以观察到一种未定义的行为:
如果我在A类中将我的成员变量设为公共,那么就不会出现任何编译错误,但是输出结果为1我不知道为什么..
class A{
public:
int a
....
....
输出:
A的施工完成
B的建设完成
一个有趣的
0027F91C
1(为什么1)并且当我尝试访问受保护的成员时没有出现错误?
答案 0 :(得分:4)
你在语法中遇到了一个小怪癖(并不是说C ++中很少有......)。访问成员变量的默认方式是直接使用名称或通过this->
。也就是说,show函数的简单拼写是:
void B::show() {
std::cout << a << std::endl; // alternatively this->a
std::cout << &a << std::endl; // &(this->a)
}
其语法简单一致。现在,该语言允许您在访问成员时放入额外的限定符来访问基类的成员:
std::cout << A::a << std::endl; // Extra qualification
这实际上相当于this->A::a
,并且额外资格的主要用途是消除歧义(如果两个基地有一个成员a
,请选择A
中的一个)和虚函数禁用动态调度的情况。
现在,您可以使用指针执行相同操作,如&this->A::a
中所示,它将获取当前对象的子对象a
中成员A
的地址。这种情况下的问题是您不能删除this->
限定符,因为保留语法&A::a
以获取指向成员的指针,尽管通过添加一组额外的括号,如{{1}解析器不能再将其解释为获取指向成员的指针,而是将&(A::a)
表示的对象的地址解释为,如前所述,它是基类中的成员。
答案 1 :(得分:3)
简短回答:没有涉及未定义的行为。你看到的行为是:
&A::a
是尝试获取指向指向A类成员a的成员的指针。如果a在A中受保护,则此表达式仅传递A类(或A的朋友)中的访问检查。在从A派生的B类中,只能通过表达式&B::a
获得指向成员的相同指针(请注意,此表达式的类型仍为int A::*
)。所以:
A::a
,则派生类B的成员函数中不允许使用表达式&A::a
。这是您的编译器错误。A::a
在A中公开,则此表达式有效,生成指向memeber的指针。 ostream
,例如使用cout << &A::a
将打印1
。这是调用ostream::operator << (bool)
的结果。你可以使用boolalpha操纵器来确定这确实是选择的重载:cout << boolalpha << &A::a
将打印true
。&(this->a)
相同),这是指向int的常规指针。对*this
基类子对象的受保护成员的此访问权限是有效的,因此即使在A中受保护,也可以使用该变体。更长的解释:
标准说(5.3.1 / 3):
一元&amp;的结果operator是指向其操作数的指针。该 操作数应为左值或合格ID。如果操作数是a qualified-id命名某个类C的非静态成员m,类型为T, 结果有类型“指向T类C类成员的指针”,是一个 prvalue指定C :: m。 [...]
因此表达式&A::a
尝试获取指向成员a的成员指针。
在下一段(5.3.1 / 4)中,详细说明只有&amp; X :: m语法产生指向成员的指针 - 既不是&(X::m)
,也不是&m
或者是{ {1}}做:
指向成员的指针仅在显式&amp;使用和它的 操作数是一个未括在括号中的限定id。
但是如果允许访问,这样的表达式才有效。如果受保护的成员(11.4 / 1)适用:
除了前面第11章中描述的那些之外的附加访问检查 在非静态数据成员或非静态成员函数时应用 是其命名类的受保护成员(11.2)如上所述 之前,因为引用而授予对受保护成员的访问权限 发生在朋友或某些C类的成员中。如果要形成 指向成员(5.3.1)的指针,嵌套名称说明符应表示C 或来自C的类[...]
在您的情况下,将授予对受保护成员的访问权限,因为对A的引用发生在从B派生的B类成员中。当表达式尝试形成指向成员的指针时,嵌套 - name-specifier (最后的“:: a”之前的部分)必须表示B.因此,最简单的允许形式是X::m
。表格&B::a
仅允许在A类成员或朋友中使用。
指向成员的指针没有格式化的输出运算符(既不是istream成员也不是自由运算符函数),因此编译器将查看可以使用标准转换(序列)调用的重载。 4.12 / 1中描述了从指针到成员的唯一标准转换:
指向成员类型的指针的prvalue可以转换为a bool类型的prvalue。转换[...]空成员指针值 为假;任何其他值都转换为true。 [...]
无需额外转换即可使用此转化来调用&A::a
。其他重载需要更长的转换序列,因此过载是最佳匹配。
由于basic_ostream<charT,traits>& basic_ostream<charT,traits>::operator<<(bool n)
获取某个成员的地址,因此它不是空成员指针值。因此它将转换为&A::a
,其打印为“1”(noboolalpha)或“true”(boolalpha)。
最后,表达式true
在B的成员中有效,即使a在上述规则中受A保护,此表达式也不形成指向成员的指针,因此上面引用的特殊访问规则确实如此不适用。对于这种情况,11.4 / 1继续:
所有其他访问都涉及(可能是隐式的)对象表达式 (5.2.5)。在这种情况下,对象表达式的类应为C. 或者来自C的类。
此处对象展示是隐式&(A::a)
,即(*this)
表示与A::a
相同。 (*this).A::a
的类型显然与发生访问的类(B)相同,因此允许访问。 [注意:B中不允许(*this)
。]
int x = A(42).a
中的&(A::a)
表示与B::show()
相同,这是指向int的普通指针。
答案 2 :(得分:-5)
这是一个语法问题。 &amp;意味着你要求在右边的元素地址。
如果你写:
&A::a
就像你写的那样
(&A)::a
这意味着你要求从'&amp; A'访问'a',这是不对的。
&amp;不仅适用于变量,也可以在函数上使用,请参阅:http://www.cprogramming.com/tutorial/function-pointers.html