为什么继承的受保护操作员=()有公共访问权限

Why does inherited protected operator=() has public access

本文关键字:访问 权限 访问权 继承 受保护 操作员 为什么      更新时间:2023-10-16

被称为"保护为受保护"的超载运算符=可以公开访问子类,将父级继承为public。

#include <iostream>
class A {
public:
    A(char c) : i(c) {}
    char i;
protected:
    A& operator=(const A& rdm) {
        std::cout << "accessing operator=()" << std::endl;
        i = 'x';
        return *this;
    }
};
class B : public A {
public:
    B(char c) : A(c) {}
};

int main(int ac, char** av) {
    B a('a');
    B b('b');
    std::cout << "a.i == " << a.i << std::endl;
    a = b;
    std::cout << "a.i == "<< a.i << std::endl;
}

编译时没有错误:

$ g++ -Wall -o test_operator ~/test_operator.cpp
$ ./test_operator
a.i == a
accessing operator=()
a.i == x

使用直接无法编译的。除operator=()以外的任何其他操作员过载不会编译。用G 4.4.7和7.3.0测试C 98和C 17。

为什么在这种情况下可以公开访问operator=()

B中有一个隐式副本分配运算符。

来自C 11标准:

如果类定义未明确声明副本分配运算符,则隐含地声明。如果类定义声明移动构造函数或移动分配运算符,则将隐式声明的副本分配运算符定义为已删除;否则,将其定义为默认设置([DCL.FCT.DEF](。如果类有用户指定的复制构造函数或用户指定的破坏者,则将后一种情况弃用。X类的隐式副本分配运算符将具有表格

X& X::operator=(const X&)

如果

- X的每个直接基类B都有一个复制分配操作员,其参数为类型const B&const volatile B&B,以及

- 对于X的所有非静态数据成员,属于类型M(或其数组(,每个类型类型都有一个复制分配运算符,其参数为 const M&const volatile M&或 CC_14或 M

否则,隐式宣布的副本分配运算符将具有表单

X& X::operator=(X&)

换句话说,您的代码表现得好像您有:

class B : public A {
public:
    B(char c) : A(c) {}
    B& operator=(B const& rhs) { A::operator==(rhs); return *this; }
};

这是您的更新版本,它演示了隐式声明的复制分配运算符函数的行为。它证明了B不继承A的复制分配操作员。

#include <iostream>
class A {
public:
    A(char c) : i(c) {}
    char i;
protected:
    A& operator=(const A& rdm) {
        std::cout << "accessing A::operator=()" << std::endl;
        i = 'x';
        return *this;
    }
};
class X
{
   public:
      X& operator=(X const& rhs)
      {
        std::cout << "accessing X::operator=()" << std::endl;
        return *this;
      }
};
class B : public A {
public:
    B(char c) : A(c) {}
    X x;
};

int main(int ac, char** av) {
    B a('a');
    B b('b');
    std::cout << "a.i == " << a.i << std::endl;
    a = b;
    std::cout << "a.i == "<< a.i << std::endl;
}

输出:

a.i == a
accessing A::operator=()
accessing X::operator=()
a.i == x

隐式声明/定义的复制分配运算符的行为就像我们有:

B& operator=(B const& rhs)
{
   A::operator==(rhs);
   this.x = rhs.x;
   return *this;
}

这与标准所说的一致:

非工会类X隐式定义的副本/移动分配操作员执行其子对象的成员复制/移动分配。X的直接基础类是在 base-Specifier-list 中以其声明的顺序分配的,然后分配了X的直接非静态数据成员它们在班级定义中被声明。