访问限定符与友元
● 使用 public/private/protected 限定类成员的访问权限
//main.cpp
struct Str
{
//结构体默认的访问权限,可以省略
//public:
int x = 0;
private:
int y = 0;
};
int main()
{
Str m_str;
//OK: 在同一个翻译单元内,结构体struct Str { ... }以外的任何地方都可以访问公有成员x
//更普遍地: 只要是公有成员,就可以在结构体外访问它
std::cout << m_str.x << std::endl;
//Error: 'y' is a private member of 'Str',即只能在结构体struct Str { ... }内访问私有成员y
//结构体Str类内声明的成员函数即使在类外定义,也算作结构体struct Str { ... }内
//更普遍地: 只要是私有成员,就只能在结构体内访问它
std::cout << m_str.y << std::endl;
return 0;
}
– 访问权限的引入使得可以对抽象数据类型进行封装
struct Str
{
//结构体的成员(比如内部数据)完全暴露给用户,导致用户可以操作这些成员,当出现成员的增删或者修改等等,会使程序崩溃或出现未定义的行为
int x = 0;
int y = 3;
};
– 类与结构体缺省访问权限的区别
struct Student
{
//当没有书写private或protected或public访问权限说明符时,以下四个成员都是公有成员,即结构体的缺省访问权限是public
char Name[10];
int Age = 0;
char Sex = 'F';
void memberfunction() { }
};
class Student
{
//当没有书写private或protected或public访问权限说明符时,以下四个成员都是私有成员,即类的缺省访问权限是private
char Name[10];
int Age = 0;
char Sex = 'F';
void memberfunction() { }
};
struct Student
{
//当有访问权限说明符时,各个访问权限生效的区域如下
private:
... //private:之后,protected:之前的成员都是私有成员
protected:
... //protected:之后,public:之前的成员都是私有成员
public:
... //public:之后,};之前的成员都是私有成员
};
//将关键字struct更换成class,以上定义仍然符合C++语法标准
● 使用友元打破访问权限限制 关键字 —— friend
int main(); //main函数的声明
class Str2;
class Str
{
friend int main(); //在类Str之前声明main函数,然后将main函数声明为类Str的友元,所以main函数内可以访问类Str中的private成员、protected成员和public成员,所以#1处的代码合法
friend Str2; //在类Str之前声明类Str2,然后将类Str2声明为类Str的友元,所以类Str2内可以访问类Str中的private成员、protected成员和public成员,所以#2处的代码合法
inline static int x = 3; //私有成员,类内访问
};
class Str2
{
public:
void fun()
{
std::cout << "Use private member 'x' of class Str in class Str2: " << Str::x << std::endl; //#2 OK
}
};
int main()
{
std::cout << Str::x << std::endl; //#1 OK
Str2 my_Str2;
my_Str2.fun();
return 0;
}
– 声明某个类或某个函数是当前类的友元 慎用!
int main();
class Str2;
class Str
{
friend int main();
friend Str2; //C++标准要求: 类Str2内可以访问类Str中所有的成员(即使是私有成员),反过来类Str不可以访问类Str2的protected成员和private成员
inline static int x = 3;
};
class Str2
{
public:
void fun()
{
std::cout << Str::x << std::endl; //OK
}
};
int main()
{
std::cout << Str::x << std::endl; //OK
Str2 my_Str2;
my_Str2.fun();
return 0;
}
//同上面的例子
class Str
{
inline static int x = 3;
};
class Str2
{
//将类Str2声明为类Str的友元,类Str2对类Str依然保持内部成员所具有的访问限制属性
friend Str;
void fun()
{
std::cout << Str::x << std::endl; //Error: 'x' is a private member of 'Str'
}
};
class Str4;
class Str
{
inline static int x = 3;
friend Str4; //OK,将Str4声明成private权限限制符合C++语法标准
};
class Str2
{
inline static int x = 4;
protected:
friend Str4; //OK,将Str4声明成protected权限限制符合C++语法标准
};
class Str3
{
inline static int x = 5;
public:
friend Str4; //OK,将Str4声明成public权限限制符合C++语法标准
};
class Str4
{
public:
void showStrX()
{
std::cout << "Str::x: " << Str::x << std::endl; //OK
}
void showStrX2()
{
std::cout << "Str2::x: " << Str2::x << std::endl; //OK
}
void showStrX3()
{
std::cout << "Str3::x: " << Str3::x << std::endl; //OK
}
};
int main()
{
Str4 myStr;
myStr.showStrX(); //输出3
myStr.showStrX2(); //输出4
myStr.showStrX3(); //输出5
return 0;
}
void fun();
class Str
{
inline static int x = 100;
friend void fun();
};
void fun()
{
std::cout << Str::x << std::endl; //OK
}
int main()
{
fun();
return 0;
}
– 在类内首次声明友元类或友元函数
class Str
{
inline static int x = 100;
friend class Str2; //首次声明友元类Str2,即使编译器由上往下解析并且之前没有类Str2的声明,编译器仍然会解析成功
friend void fun(); //首次声明友元函数fun(),同上,编译器将fun()解析为友元函数
};
class Str2
{
};
void fun()
{
}
● 注意使用限定名称引入友元并非友元类(友元函数)的声明
void function(); //#1
class Str
{
inline static int x = 100;
friend class Str2; //首次声明友元类Str2,即使编译器由上往下解析并且之前没有类Str2的声明,编译器仍然会解析成功
friend void fun(); //首次声明友元函数fun(),同上,编译器将fun()解析为友元函数
friend void ::function(); //编译器不会将::function()解析为function()的声明,所以必须在#1处加上function()的声明
};
– 友元函数的类外定义与类内定义
class Str
{
inline static int x = 100;
friend class Str2 //Error: Cannot define a type in a friend declaration
{
}
};
class Str
{
int x = 100;
friend void fun();
};
void fun() //友元函数的类外定义
{
Str val;
std::cout << val.x << std::endl;
}
int main()
{
fun();
return 0;
}
– 隐藏友元( hidden friend ):常规名称查找无法找到(The Power of Hidden Friends in C++)
class Str
{
int x = 100;
friend void fun() //fun()不是类Str的成员,是类Str的隐藏友元函数
{
Str val;
std::cout << val.x << std::endl;
}
};
int main()
{
fun(); //全局域中无fun()的声明,常规名称查找无法找到,报错
return 0;
}
● 好处:减轻编译器负担,防止误用
void fun(); //全局域中声明fun()或者在类内声明隐藏友元类外定义隐藏友元,见友元函数的类外定义
class Str
{
int x = 100;
friend void fun() //fun()不是类Str的成员,是类Str的隐藏友元
{
Str val;
std::cout << val.x << std::endl;
}
};
int main()
{
fun(); //OK
return 0;
}
● 改变隐藏友元的缺省行为:在类外声明或定义函数
class Str
{
int x = 100;
friend void fun(const Str& val) //隐藏友元有一个传入参数
{
std::cout << val.x << std::endl;
}
};
int main()
{
Str myStr;
fun(myStr); //对fun()传入了Str类型的参数myStr,常规名称查找无法找到后,触发实参类型依赖查找ADL(Argument Dependent Lookup),会在类Str内检测是否有匹配的函数
return 0;
}
参考
深蓝学院:C++基础与深度解析