为啥继承的受保护运算符=()具有公共访问权限

Posted

技术标签:

【中文标题】为啥继承的受保护运算符=()具有公共访问权限【英文标题】:Why does inherited protected operator=() has public access为什么继承的受保护运算符=()具有公共访问权限 【发布时间】:2018-03-30 16:32:28 【问题描述】:

声明为受保护的重载运算符= 对继承父类的子类公开访问。

#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

直接使用 A 不会编译。除了operator=() 之外的任何其他运算符重载都不会编译。 使用 g++ 4.4.7 和 7.3.0 以及 c++98 和 c++17 进行测试。

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

【问题讨论】:

而隐式运算符调用基类运算符。 三法则再次来袭。 你会保护它吗,编译器会不会编译失败,但会提示正在发生的事情。 【参考方案1】:

B 中有一个隐式复制赋值运算符,具有 public 访问权限。

来自the C++11 Standard:

如果类定义没有显式声明复制赋值运算符,则隐式声明一个。如果类定义声明了移动构造函数或移动赋值运算符,则隐式声明的复制赋值运算符被定义为删除;否则,它被定义为默认值 ([dcl.fct.def])。如果类具有用户声明的复制构造函数或用户声明的析构函数,则不推荐使用后一种情况。类 X 的隐式声明的复制赋值运算符将具有以下形式

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

如果

——X 的每个直接基类 B 都有一个复制赋值运算符,其参数类型为 const B&amp;const volatile B&amp;B,并且

——对于 X 的所有非静态数据成员,它们属于类类型 M(或其数组),每个此类类型都有一个复制赋值运算符,其参数类型为 const M&amp;,@ 987654337@或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;

这与standard says:

非联合类X 的隐式定义的复制/移动赋值运算符执行其子对象的成员复制/移动赋值。首先分配X 的直接基类,按照它们在base-specifier-list 中的声明顺序,然后分配X 的直接非静态数据成员,在它们在类定义中的声明顺序。

【讨论】:

A 类 X 具有自己的 operator= 显示消息,以及 B 中类 X 的私有成员,可以帮助理解它不仅仅是继承的运算符:ideone.com/pJRpNP 你知道标准中的默认方法实现在哪里吗? @Jean,你可以在timsong-cpp.github.io/cppwp/n3337/class.copy#28找到它。 哦,对了。我实际上正在寻找第 29 项!谢谢你的回答。

以上是关于为啥继承的受保护运算符=()具有公共访问权限的主要内容,如果未能解决你的问题,请参考以下文章

为啥我不能访问派生构造函数的成员初始化列表中继承的受保护字段?

Python - 访问类的受保护成员_

访问基类的受保护成员

为啥我不能访问子类中的受保护变量?

策略继承和不可访问的受保护成员

为啥我不能访问静态多态派生类中的受保护成员?