5.9.2. 访问控制
函数perform_or_defer_access_check的行为取决于deferred_access_stack栈顶成员的延迟类型。如果栈顶成员是类型dk_deferred,那么对该函数的每次调用,实参binfo及decl将被链入deferred_access_checks。这些检查被缓存起来,由后来的perform_deferred_access_checks或者perform_or_defer_access_check的另一次调用,来执行验证。
注意到在上一节,在pop_to_parent_deferring_access_checks中,在调用下面这个函数的时候,deferred_access_checks的栈顶节点暂时被移走了,它由第二个节点来控制,这个节点通常代表上一级的作用域。
273 void
274 perform_or_defer_access_check (tree binfo, tree decl) in semantics.c
275 {
276 tree check;
277
278 my_friendly_assert (TREE_CODE (binfo) == TREE_VEC, 20030623);
279
280 /* If we are not supposed to defer access checks, just check now. */
281 if (deferred_access_stack->deferring_access_checks_kind == dk_no_deferred)
282 {
283 enforce_access (binfo, decl);
284 return;
285 }
286 /* Exit if we are in a context that no access checking is performed. */
287 else if (deferred_access_stack->deferring_access_checks_kind == dk_no_check)
288 return;
289
290 /* See if we are already going to perform this check. */
291 for (check = deferred_access_stack->deferred_access_checks;
292 check;
293 check = TREE_CHAIN (check))
294 if (TREE_VALUE (check) == decl && TREE_PURPOSE (check) == binfo)
295 return;
296 /* If not, record the check. */
297 deferred_access_stack->deferred_access_checks
298 = tree_cons (binfo, decl,
299 deferred_access_stack->deferred_access_checks);
300 }
如果访问检查不需要被推迟,那么对由binfo指定的作用域中的decl的访问,立即执行访问检查。同样函数perform_deferred_access_checks也可执行所有的延迟访问检查。
258 void
259 perform_deferred_access_checks (void) in semantics.c
260 {
261 tree deferred_check;
262 for (deferred_check = deferred_access_stack->deferred_access_checks;
263 deferred_check;
264 deferred_check = TREE_CHAIN (deferred_check))
265 /* Check access. */
266 enforce_access (TREE_PURPOSE (deferred_check),
267 TREE_VALUE (deferred_check));
268 }
这2个函数都依赖enforce_access来执行访问检查。
3864 bool
3865 enforce_access (tree basetype_path, tree decl) in call.c
3866 {
3867 my_friendly_assert (TREE_CODE (basetype_path) == TREE_VEC, 20030624);
3868
3869 if (!accessible_p (basetype_path, decl))
3870 {
3871 if (TREE_PRIVATE (decl))
3872 cp_error_at ("`%+#D' is private", decl);
3873 else if (TREE_PROTECTED (decl))
3874 cp_error_at ("`%+#D' is protected", decl);
3875 else
3876 cp_error_at ("`%+#D' is inaccessible", decl);
3877 error ("within this context");
3878 return false;
3879 }
3880
3881 return true;
3882 }
这里在accessible_p中,参数decl是一个从type类型基类来的声明,type是声明了decl的类。例如:
class A {
int a;
} cA;
cA.a = 5;
当进行访问控制检查时,A将是type,而a将是decl。
924 int
925 accessible_p (tree type, tree decl) in search.c
926 {
927 tree binfo;
928 tree t;
929 tree scope;
930 access_kind access;
931
932 /* Nonzero if it's OK to access DECL if it has protected
933 accessibility in TYPE. */
934 int protected_ok = 0;
935
936 /* If this declaration is in a block or namespace scope, there's no
937 access control. */
938 if (!TYPE_P (context_for_name_lookup (decl)))
939 return 1;
940
941 /* There is no need to perform access checks inside a thunk. */
942 scope = current_scope ();
943 if (scope && DECL_THUNK_P (scope))
944 return 1;
945
946 /* In a template declaration, we cannot be sure whether the
947 particular specialization that is instantiated will be a friend
948 or not. Therefore, all access checks are deferred until
949 instantiation. However, PROCESSING_TEMPLATE_DECL is set in the
950 parameter list for a template (because we may see dependent types
951 in default arguments for template parameters), and access
952 checking should be performed in the outermost parameter list. */
953 if (processing_template_decl
954 && (!processing_template_parmlist || processing_template_decl > 1))
955 return 1;
956
957 if (!TYPE_P (type))
958 {
959 binfo = type;
960 type = BINFO_TYPE (type);
961 }
962 else
963 binfo = TYPE_BINFO (type);
首先确认decl不是在一个块里(FOR或WHILE块等),或者在名字空间中,这些域里都没有访问控制实施。函数context_for_name_lookup返回decl将首先被查找的绑定域。611行的ANON_AGGR_TYPE_P表示的是匿名的union或struct类型(这是ISO C++不允许的),根据【3】,“一个匿名union的成员名字应该区别于,该匿名union声明所在域中的其它实体名字。出于名字查找的目的,在匿名union定义的后面,其成员被视为定义在其声明所在的域中。”因此,需要上升一级。
600 tree
601 context_for_name_lookup (tree decl) in search.c
602 {
603 /* [class.union]
604
605 For the purposes of name lookup, after the anonymous union
606 definition, the members of the anonymous union are considered to
607 have been defined in the scope in which the anonymous union is
608 declared. */
609 tree context = DECL_CONTEXT (decl);
610
611 while (context && TYPE_P (context) && ANON_AGGR_TYPE_P (context))
612 context = TYPE_CONTEXT (context);
613 if (!context)
614 context = global_namespace;
615
616 return context;
617 }
如果在模板声明(不是具现)中,同样跳过访问控制检查。在调用accessible_p时,参数type可以是*_TYPE或binfo节点,因此如果decl所在域执行访问控制检查,需要妥善设定type及binfo。
accessibe_p (continue)
965 /* [class.access.base]
966
967 A member m is accessible when named in class N if
968
969 --m as a member of N is public, or
970
971 --m as a member of N is private, and the reference occurs in a
972 member or friend of class N, or
973
974 --m as a member of N is protected, and the reference occurs in a
975 member or friend of class N, or in a member or friend of a
976 class P derived from N, where m as a member of P is private or
977 protected, or
978
979 --there exists a base class B of N that is accessible at the point
980 of reference, and m is accessible when named in class B.
981
982 We walk the base class hierarchy, checking these conditions. */
983
984 /* Figure out where the reference is occurring. Check to see if
985 DECL is private or protected in this scope, since that will
986 determine whether protected access is allowed. */
987 if (current_class_type)
988 protected_ok = protected_accessible_p (decl, current_class_type, binfo);
989
990 /* Now, loop through the classes of which we are a friend. */
991 if (!protected_ok)
992 protected_ok = friend_accessible_p (scope, decl, binfo);
993
994 /* Standardize the binfo that access_in_type will use. We don't
995 need to know what path was chosen from this point onwards. */
996 binfo = TYPE_BINFO (type);
997
998 /* Compute the accessibility of DECL in the class hierarchy
999 dominated by type. */
1000 access = access_in_type (type, decl);
1001 if (access == ak_public
1002 || (access == ak_protected && protected_ok))
1003 return 1;
1004 else
1005 {
1006 /* Walk the hierarchy again, looking for a base class that allows
1007 access. */
1008 t = dfs_walk (binfo, dfs_accessible_p, dfs_accessible_queue_p, 0);
1009 /* Clear any mark bits. Note that we have to walk the whole tree
1010 here, since we have aborted the previous walk from some point
1011 deep in the tree. */
1012 dfs_walk (binfo, dfs_unmark, 0, 0);
1013
1014 return t != NULL_TREE;
1015 }
1016 }
上面的注释解释了几种可访问的情况:1)类的公有成员;2)虽然是私有成员,但通过友元来访问;3)是保护成员,但是通过派生类来访问;4)在引用处,存在可访问的基类,通过该基类可以访问该指定的成员。对于第4点,为了便于理解,我们给出一个例子:
class base {
public:
int i;
};
class D: public base {
private:
using base::i;
};
int main () {
D d;
d.i = 4; // error: ‘nt base::i’ inaccessible
base *pb = &d;
pb->i = 5; // OK
return 1;
}
下面开始逐个来检查。