文章目录
(C++ Std 学习) 访问说明符的理解和运用(public, protected, private)
概述和背景
自己平时写代码的过程中,其实是经常是会用面向对象的基本的操作的,就是访问控制说明嘛,有些变量不想暴露,只提供公共的接口之类的,有些接口和变量想要有子类继承或者重写,就能够提供一个基本的访问控制嘛,用了这么久,也没个时间好好的认真地看看官方文档的一些解释和定义,这次就想抽个时间好好的学习学习,就从官方文档开始了,因此就有了这篇学习的文档了。
参考文本和自行翻译
声明: 下面这节是从英文文档中我自己想办法翻译过来学习用的!
下面这一整节主要来自于从C++ Reference里面的学习和翻译的过程:
定义
访问说明符
在类/结构或联合的成员规范中,定义后续成员的可访问性。
在派生类声明的基说明符中,定义后续基类的继承成员的可访问性。
- 在访问说明符之后声明的成员具有公共成员访问权限;
- 在访问说明符之后声明的成员具有受保护的成员访问;
- 在访问说明符之后声明的成员具有私有成员访问权限;
- 公共继承:在访问说明符之后列出的基类的公共成员和受保护成员将其成员访问权限保留在派生类中,而派生类无法访问基类的私有成员;
- 受保护的继承:在访问说明符之后列出的基类的公共成员和受保护成员是派生类的受保护成员,而派生类无法访问基类的私有成员;
- 私有继承:在访问说明符之后列出的基类的公共成员和受保护成员是派生类的私有成员,而派生类无法访问基类的私有成员。
案例代码分析
每个类成员的名称(静态,非静态,函数,类型等)都有一个关联的“成员访问权”。 当在程序的任何位置使用成员的名称时,将检查其访问权限,如果不满足访问规则,则该程序不会编译:
#include <iostream>
class Example {
public: // all declarations after this point are public(此后的所有声明都是公开的)
void add(int x) { // member "add" has public access(成员“添加”具有公共访问权限)
n += x; // OK: private Example::n can be accessed from Example::add
// OK:私人Example :: n可以从Example :: add访问
}
private: // all declarations after this point are private(此后的所有声明都是私有的)
int n = 0; // member "n" has private access(成员“ n”具有私人访问权限)
};
int main() {
Example e;
e.add(1); // OK: public Example::add can be accessed from main
// OK:public示例:: add可以从main访问.
//e.n = 7; // error: private Example::n cannot be accessed from main
//e.n = 7; //错误:私有示例:: n无法从main访问
}
访问说明符使类的作者可以决定类的用户可以访问哪些类成员(即接口类的内容),以及哪些成员供该类的内部使用(实现类的内容)。
细节
类的所有成员(成员函数的主体,成员对象的初始化程序以及整个嵌套的类定义)都可以访问该类可以访问的所有名称。 成员函数中的本地类可以访问该成员函数可以访问的所有名称。
- 默认情况下,用关键字class(类)定义的类对其成员及其基类具有私有访问权限。
- 默认情况下,用关键字struct(结构体)定义的类对其成员及其基类具有公共访问权限。
- 一个union(联合体)默认情况下对其成员具有公共访问权限。
为了向受保护或私有成员授予访问其他功能或类的权限,可以使用友元(friend class)声明。
可访问性适用于所有名称,而与它们的来源无关,因此将检查由typedef引入或使用声明的名称,而不是它所引用的名称:
class A : X {
class B {}; // B is private in A(B 在 A中是私有的)
public:
typedef B BB; // BB is public(BB 拥有了public访问权限)
};
void f() {
A::B y; // error: A::B is private(A中B是私有的不能访问)
A::BB x; // OK: A::BB is public(BB是共有的可以访问)
}
成员访问不会影响可见性:私有和私有继承成员的名称是可见的,都能够被其他人通过头文件直接查看,而且通过重载解析加以考虑,仍会考虑对不可访问的基类的隐式转换,等等。成员访问检查是解释任何给定语言构造之后的最后一步。 该规则的目的是,用公有变量替换任何私有绝不会改变程序的任何行为。
对默认函数参数以及默认模板参数中使用的名称的访问检查是在声明时执行的,而不是在使用时执行的。
在调用点使用表达式的类型检查虚拟函数名的访问规则,表达式用于表示调用成员函数的对象。忽略对最终重写器的访问:
struct B { virtual int f(); }; // f is public in B
class D : public B { private: int f(); }; // f is private in D
void f() {
D d;
B& b = d;
b.f(); // OK: B::f is public, D::f is invoked even though it's private
d.f(); // error: D::f is private
}
根据非限定名称查找,私有名称可以通过限定名称查找来访问:
class A {};
class B : private A {};
class C : public B {
A* p; // error: unqualified name lookup finds A as the private base of B
::A* q; // OK: qualified name lookup finds the namespace-level declaration
};
可以通过继承图中的多个路径可访问的名称具有最多访问权限的路径的访问权限:
class W { public: void f(); };
class A : private virtual W {};
class B : public virtual W {};
class C : public A, public B {
void f() { W::f(); } // OK: W is accessible to C through B
};
一个类中可以以任何顺序出现任意数量的访问说明符。成员访问说明符可能影响类布局:非静态数据成员的地址只保证在不由访问说明符(直到C++ 11)分隔的成员(以C++ 11)分隔时,以声明的顺序增加。
对于StandardLayoutType,所有非静态数据成员必须具有相同的访问权限。(C++ 11)
在同一类中重新声明成员时,必须在同一成员访问权限下执行此操作:
struct S {
class A; // S::A is public
private:
class A {}; // error: cannot change access
};
公共成员访问
公共成员构成类的公共接口的一部分(公共接口的其他部分是ADL发现的非成员函数)。
类的公共成员可以在任何地方访问:
class S {
public: // n, E, A, B, C, U, f are public members
int n;
enum E {A, B, C};
struct U {};
static void f() {}
};
int main() {
S::f(); // S::f is accessible in main
S s;
s.n = S::B; // S::n and S::B are accessible in main
S::U x; // S::U is accessible in main
}
保护成员访问
受保护成员构成类与其派生类的接口(与类的公共接口不同)。
类的受保护成员只能访问
- 只能给类的成员或者友元类访问;
- 对该类的任何派生类的成员和友元类(直到C++ 17),但只有当访问受保护成员的对象的类是派生类或派生类的派生类时才被允许:
struct Base {
protected:
int i;
private:
void g(Base& b, struct Derived& d);
};
struct Derived : Base {
void f(Base& b, Derived& d) { // member function of a derived class
++d.i; // OK: the type of d is Derived
++i; // OK: the type of the implied '*this' is Derived
// ++b.i; // error: can't access a protected member through
// Base (otherwise it would be possible to change
// other derived classes, like a hypothetical
// Derived2, base implementation)
}
};
void Base::g(Base& b, Derived& d) { // member function of Base
++i; // OK
++b.i; // OK
++d.i; // OK
}
void x(Base& b, Derived& d) { // non-member non-friend
//++b.i; // error: no access from non-member
//++d.i; // error: no access from non-member
}
形成指向受保护成员的指针时,它必须在其声明中使用派生类:
struct Base {
protected:
int i;
};
struct Derived : Base {
void f() {
// int Base::* ptr = &Base::i; // error: must name using Derived
int Base::* ptr = &Derived::i; // OK
}
};
私有成员访问
私有成员构成类的实现,以及类的其他成员的私有接口。类的私有成员只能由该类的成员和友元访问,而不管这些成员是在同一实例上还是在不同实例上:
class S {
private:
int n; // S::n is private
public:
S() : n(10) {} // this->n is accessible in S::S
S(const S& other) : n(other.n) {} // other.n is accessible in S::S
};
显式强制转换(C样式和函数样式)允许从派生左值强制转换为对其私有基的引用,或者从指向派生的指针强制转换为指向其私有基的指针。
自己编写的代码中使用分析
///
// Copyright (c)2020, Tom Zhao personal. ("TZOpenTools")
// This software is a personal tools project by Tom Zhao.
// Description:
///
#ifndef _TZSOFTS_REF_COUNTER_H_H_
#define _TZSOFTS_REF_COUNTER_H_H_
#include "tzsofts_platform_utils.h"
#pragma pack(push, 8)
TZ_NAMESPACE_BEGIN(TzSoft)
typedef int RefCounterType;
class RefCounter
{
public:
RefCounter()
: m_refs(0)
{
}
RefCounter(RefCounterType n)
: m_refs(0)
{
}
~RefCounter()
{
}
RefCounter& operator=(RefCounterType n)
{
m_refs = 0;
TzInterlockedExchange(&m_refs, n);
}
inline operator RefCounterType() const
{
return TzInterlockedExchangeAdd(const_cast<RefCounterType *>(&m_refs), 0);
}
inline RefCounterType operator++()
{
return TzInterlockedIncrement(&m_refs);
}
inline bool operator==(const RefCounter& other) const
{
return m_refs == other.m_refs;
}
inline bool operator==(RefCounterType n) const
{
return (m_refs == n);
}
friend inline bool operator==(RefCounterType a, const RefCounter& b)
{
return a == b.m_refs;
}
inline bool operator!=(const RefCounter& other) const
{
return m_refs != other.m_refs;
}
inline bool operator!=(RefCounterType n) const
{
return (m_refs != n);
}
friend inline bool operator!=(RefCounterType a, const RefCounter& b)
{
return a != b.m_refs;
}
inline bool operator>(const RefCounter& other) const
{
return m_refs > other.m_refs;
}
inline bool operator>(RefCounterType n) const
{
return (m_refs > n);
}
friend inline bool operator>(RefCounterType a, const RefCounter& b)
{
return a > b.m_refs;
}
inline bool operator>=(const RefCounter& other) const
{
return m_refs >= other.m_refs;
}
inline bool operator>=(RefCounterType n) const
{
return (m_refs >= n);
}
friend inline bool operator>=(RefCounterType a, const RefCounter& b)
{
return a >= b.m_refs;
}
inline bool operator<(const RefCounter& other) const
{
return m_refs < other.m_refs;
}
inline bool operator<(RefCounterType n) const
{
return (m_refs < n);
}
friend inline bool operator<(RefCounterType a, const RefCounter& b)
{
return a < b.m_refs;
}
inline bool operator<=(const RefCounter& other) const
{
return m_refs <= other.m_refs;
}
inline bool operator<=(RefCounterType n) const
{
return (m_refs <= n);
}
friend inline bool operator<=(RefCounterType a, const RefCounter& b)
{
return a <= b.m_refs;
}
private:
volatile RefCounterType m_refs;
};
TZ_NAMESPACE_END(TzSoft)
#pragma pack(pop)
#endif
上述代码中是一个简单的引用计数类型的封装,有一个私有变量,m_refs
围绕这个私有变量定义里一系列类型比较的操作提供的是公有访问接口的权限,这样的代码在面向对象的编程过程中非常的,我就不过多的列举了,可以尝试在自己日常编码过程中,多去思考关于访问说明符的运用的一些关系,以及如何更好的利用友元完成一些方便自己的操作。
个人格言
用心去感受你自己需要坚持的生活,未来慢慢会给你答案的。