在设计和实现当前类时,实现拷贝构造函数和赋值构造函数时,甚至在成员函数的内部是可以直接访问当前类的对象(以参数的形式)的私有成员变量的。考虑如下的代码:
class Test
{
public:
Test(int );
Test(const Test& );
Test& operator=(const Test& );
void print(const Test& );
private:
int _v;
};
Test::Test(int v):_v(v)
{}
Test::Test(const Test& other):_v(other._v)
{}
Test& Test::operator=(const Test& other)
{
if (this == &other)
return *this;
_v = other._v;
return *this;
}
void Test::print(const Test& other)
{
std::cout << other._v << std::endl;
}
int main()
{
Test t = 1;
// t._v = 5; 编译出错
return 0;
}
在实现当前类时,可通过当前类的对象访问其私有成员变量,注意必须是当前类的对象,而不可以是其他类的对象实例,(如果想要访问其他类的私有成员的话,或者其他类的内部开放私有成员的访问接口,或者将当前类作为其友元)。
之所以 C++ 支持这样的语法,就在于,封装是编译期的概念,是针对类型而非对象的,故在类的成员函数(而非调用实例的成员函数)中可以访问同类型实例对象的私有成员变量。
关于_v
私有成员变量的符号解析开始说起:
确定符号的查找域
如
operator=
的_v = other._v
,当编译器发现_v
时,它会在_v
变量所属的对象other
的类域中寻找该符号。确定当前域中哪些符号可以访问
有第一步可知,当前查找的域是类域,而
print
函数在Test
类体中,故print
可以访问Test
类体的所有变量。
而函数main
不在Test
类体中,故main
函数不可访问Test
类域中的私有成员变量符号已查到,编译通过
类成员变量的访问权限是编译器强加的,编译器可以找到
_v
。
直觉上,我们会误以为operator=
中的_v = other._v
中的_v
的查找域应该是对象other对应的作用域,而C++编译器却是在对象other的类域查找_v
符号。
References
[1] 【C++】 私有成员变量的理解