【问题标题】:Why can't I access a protected member variable of a base class passed into a function as an argument?为什么我不能访问作为参数传递给函数的基类的受保护成员变量?
【发布时间】:2012-12-05 12:24:46
【问题描述】:

This 答案似乎表明它应该可以工作,那么为什么我的示例会引发编译器错误:

class Class1
{
protected:
    long m_memberVar;
};

class SubClass1: public Class1
{
public:
    void PrintMember(Class1 memberToPrintFrom)
    {
        Console::Write("{0}", memberToPrintFrom.m_memberVar); // <-- Compiler error: error C2248: 'BaseClassMemberAccess::Class1::m_memberVar' : cannot access protected member declared in class 'BaseClassMemberAccess::Class1'
    }
};

[编辑] - 根据 Need4Sleep 的建议将子类更改为公共继承,但没有区别。

【问题讨论】:

标签: c++ inheritance


【解决方案1】:

在此答案中,我假设您在代码中使用了 public 继承(问题中没有提及)。


[C++11: 11.2/1]: 如果使用 public 访问说明符将一个类声明为另一个类的基类(第 10 条),则基类的 public 成员可以作为派生类的public 成员和基类的protected 成员可作为派生类的protected 成员 访问。如果使用protected 访问说明符将一个类声明为另一个类的基类,则基类的publicprotected 成员可以作为派生类的protected 成员进行访问。如果使用private 访问说明符将一个类声明为另一个类的基类,则基类的公共成员和protected 成员可以作为派生类的private 成员访问。

这涵盖了您正在访问 same 对象的成员的情况。

然而,protected 成员访问有点好奇,为了访问 another 对象的 protected 成员,它必须位于 same 的定义内 类型或 更多派生 类型;在您的情况下,它是派生较少的类型(即基础):

[C++11: 11.4/1]: 当非静态数据成员或非静态成员函数是其命名类 (11.2) 的受保护成员时,将应用第 11 节中所述之外的附加访问检查。如前所述,访问授予受保护成员是因为引用发生在某个类C 的朋友或成员中。如果访问要形成指向成员的指针 (5.3.1),nested-name-specifier 应表示C 或派生自C 的类。所有其他访问都涉及(可能是隐式的)对象表达式(5.2.5)。 在这种情况下,对象表达式的类应为C或派生自C的类。

也就是说,您必须在 Class1 成员函数中运行此代码。

Bjarne 在他的书 The C++ Programming Language (Sp. Ed.) 第 404 页上提到了这一点:

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

【讨论】:

【解决方案2】:

派生类只能通过其自身 (this) 或同一类的另一个对象访问基类的受保护成员,但一般不能通过基类访问。那就是访问,目的是成员的使用被认为仅限于类的实现细节,并且由于您的类正在处理基类,因此在这种特殊情况下它不会知道成员的含义.

您可以使用一种解决方法来获取访问权限,即在基类中提供一个受保护的 getter 和 setter,通常是静态的,它将为您获取或设置它。

class Class1
{
     protected:
       long m_memberVar; // could even be private

       static long getMemberVar( Class1 const& inst )
       {
          return inst.m_memberVar;
       }

       static long setMemberVar( Class1 & inst, long val )
       {
          inst.m_memberVar = val;
       }

};

现在派生类(但不是通用类)可以使用 getter 和 setter 方法。

【讨论】:

    【解决方案3】:

    您还可以利用派生对象可以转换为基本对象类型以及对象可以访问其自身类型的任何对象的受保护和私有成员这一事实。如果基础对象有一个赋值运算符,可以保证所有需要的成员都被正确复制,你可以这样做:

    class Class1
    {
    protected:
        long m_memberVar;
    };
    
    class SubClass1 : public Class1
    {
    public:
        void PrintMember(Class1 memberToPrintFrom)
        {
            SubClass1 tmpSC;
            auto tmpC1 = dynamic_cast<Class1*>(&tmpSC);
            *tmpC1 = memberToPrintFrom;
    
            cout << tmpSC.m_memberVar << endl;
        }
    };
    

    这效率不高,但可以让您获得基类成员,而无需向基类添加函数。这是使用对象切片将临时派生对象的基础部分替换为传递的基础对象的值。

    【讨论】:

      猜你喜欢
      • 2011-02-03
      • 2013-03-05
      • 2011-10-06
      • 2015-06-09
      • 1970-01-01
      • 1970-01-01
      • 2018-10-21
      • 1970-01-01
      • 2016-10-01
      相关资源
      最近更新 更多