问题 父类中的受保护数据在子类中不可用?


我很困惑:我认为受保护的数据是由C ++中给定类的子进程读/写的。

以下代码段无法在MS Compiler中编译

class A
{
protected:
  int data;
};

class B : public A
{
  public:

  B(A &a)
  {
    data = a.data;
  }
};

int main()
{
  A a;
  B b = a;
  return 0;
}

错误信息:

Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

demoFail.cpp
demoFail.cpp(12) : error C2248: 'A::data' : cannot access protected member declared in class 'A'
        demoFail.cpp(4) : see declaration of 'A::data'
        demoFail.cpp(2) : see declaration of 'A'

我究竟做错了什么?


11324
2017-09-12 06:53


起源



答案:


根据TC ++ PL,第404页:

派生类只能为其自身类型的对象访问基类'受保护成员....这可以防止在一个派生类破坏属于其他派生类的数据时可能发生的细微错误。

当然,这是一个简单的方法来解决这个问题:

class A
{
protected:
    int data;
};

class B : public A
{
public:
    B(const A &a)
        : A(a)
    {
    }
};

int main()
{
    A a;
    B b = a;
    return 0;
}

10
2017-09-12 07:05



Umph。确切地说,这种情况只是一个虚拟案例来证明问题。我实际上想要对传入的数据做一些相当密集的阅读。 - Paul Nathan
在任何情况下,您只能使用此B或另一个B对象的A部分中的受保护数据。如果您的A实例或C实例也派生自A,则B对这些实例没有特殊权限。如果你不能接受这个建议(通过将它复制到这个B的基础部分,你可以访问它,使这个B的一部分),那么你可能有一个设计问题,可能需要更大的图片来回答。 - UncleBens
@rlbond:睡前再次打击编码。这确实解决了我的问题。谢谢。 :) - Paul Nathan


答案:


根据TC ++ PL,第404页:

派生类只能为其自身类型的对象访问基类'受保护成员....这可以防止在一个派生类破坏属于其他派生类的数据时可能发生的细微错误。

当然,这是一个简单的方法来解决这个问题:

class A
{
protected:
    int data;
};

class B : public A
{
public:
    B(const A &a)
        : A(a)
    {
    }
};

int main()
{
    A a;
    B b = a;
    return 0;
}

10
2017-09-12 07:05



Umph。确切地说,这种情况只是一个虚拟案例来证明问题。我实际上想要对传入的数据做一些相当密集的阅读。 - Paul Nathan
在任何情况下,您只能使用此B或另一个B对象的A部分中的受保护数据。如果您的A实例或C实例也派生自A,则B对这些实例没有特殊权限。如果你不能接受这个建议(通过将它复制到这个B的基础部分,你可以访问它,使这个B的一部分),那么你可能有一个设计问题,可能需要更大的图片来回答。 - UncleBens
@rlbond:睡前再次打击编码。这确实解决了我的问题。谢谢。 :) - Paul Nathan


C ++标准说明了受保护的非静态成员 11.5/1

当派生类的朋友或成员函数引用受保护的非静态成员函数或受保护的基类的非静态数据成员时,除了前面第11章中描述的那些之外,还应用访问检查。除非形成指向成员的指针(5.3) .1),访问必须通过指向,引用或派生类本身的对象(或从该类派生的任何类)(5.2.5)。如果访问要形成指向成员的指针,则nested-name-specifier应命名派生类(或从该类派生的任何类)。

除了修复其他人之前提到的东西(构造函数) B 是私人的),我认为rlbond的方式会做得很好。但是,标准的上一段的直接后果是使用成员指针可能会出现以下情况,当然可能是类型系统中的一个洞,当然

class B : public A {
public:
  B(A &a){
    int A::*dataptr = &B::data;
    data = a.*dataptr;
  }
};

当然,不建议使用此代码,但会显示您 能够 访问它,如果你真的需要(我已经看到这种方式用于打印出来的 std::stackstd::queuestd::priority_queue 通过访问其受保护的容器成员 c


2
2017-09-12 10:48



嗨Johannes Schaub - litb你可以告诉我如何访问std :: stack或std :: queue中的受保护成员c我有访问它的问题它给了我一个错误C2248:'std :: stack <_Ty> :: c ':无法访问类'std :: stack <_Ty>'中声明的受保护成员,谢谢


你刚才 不能 复制一份 A 对象 B 构造函数。目的是离开初始化 A它的成员是它自己的构造函数:

struct A { 
  A( const A& a ): data( a.data ) {}
  protected: int data; 
};

struct B : public A {
  B( const A& a ): A( a ) {}
};

1
2017-09-12 11:18





B的构造函数是私有的。如果未在类中指定任何内容,则默认修饰符为private(对于结构,它是公共的)。所以在这个例子中问题是你不能构造B.当你向构造函数B添加public时,会出现anoter问题:

在这种情况下,B有权修改它所衍生的A部分而不是另一部分。

你可以这样做:

class A
{
public:
  A()
      : data(0)
  {
  }
  A(A &a)
  {
    data = a.data;
  }
protected:
  int data;
};

class B : public A
{
public:
  B(A &a)
      : A(a)
  {
  }
};

int main()
{
  A a;
  B b = a;
  return 0;
}

0
2017-09-12 06:59



这就是我的想法,但这不是问题。 - rlbond
@jde:实际上这是一个错字。使B public的构造函数产生相同的错误。 - Paul Nathan
@Paul,请在问题中解决任何意外的错别字,所以进一步的答案不提出相同的解决方案:) - Johannes Schaub - litb