Can't access protected member variables of the most base class through std::unique_ptr in diamond

烂漫一生 提交于 2019-12-04 06:00:40

问题


I am not an advanced programmer. Suppose there is a classic diamond inheritance:

class Base
class A: virtual public Base
class B: virtual public Base
class Last: public A, public B

Suppose Base has a variable, m_x, that is common to both A and B, such that only one of A, or B, can be called at a time, not both (which is what is needed). To get around this, this is used:

class Last: public A, public B
{
private:
    std::unique_ptr<Base> m_p;
public:
    Last(int i)
    {
        if (i)
            m_p = std::unique_ptr<Base>(new A());
        else
            m_p = std::unique_ptr<Base>(new B());
    }
};

This is fine, but now m_p->m_x cannot be accessed anymore because it says it's protected, but both A and B call m_x in their constructors directly, with no problems.

Is this a known limitation or is this the wrong way to do it? If it's wrong, what solutions are there?


Here is some code based on the diagram found here (a bit lower on the page):

#include <iostream>
#include <memory>

class Power
{
protected:
    double m_x;
public:
    Power() {}
    Power(double x): m_x {x} {}
    virtual ~Power() = default;
};

class Scanner: virtual public Power
{
public:
    Scanner() {}
    Scanner(double x): Power(x) {} // scan document
};

class Printer: virtual public Power
{
public:
    Printer() {}
    Printer(double x): Power(x) {} // print document
};

class Copier: public Scanner, public Printer
{
private:
    std::unique_ptr<Power> m_p;
public:
    Copier() {}
    Copier(double x, int i)
    {
        if (i)
            m_p = std::unique_ptr<Power>(new Scanner(x));
        else
            m_p = std::unique_ptr<Power>(new Printer(x));
    }
    void print() { std::cout << this->Power::m_x << '\n'; }
};

int main(int argc, char *argv[])
{
    Copier *copier {new Copier(1.618, 0)};
    copier->print();
    copier = new Copier(3.14, 1);
    copier->print();

    return 0;
}

Using both this->m_p and this->Power::m_x (according to answers and comments) compiles, but the output is 0.


To be sure I spell it out all: not only I am quite a beginner, but, given the example above, it oesn't really have to stay that way if there is another alternative to call Scanner or Printer only one at a time from inside Copier. I am not asking for opinions, I understand it's forbidden, but I won't reject them coming from more experienced users. After all, I am learning.


回答1:


Both virtual inheritance and std::unique_ptr are red herrings. The problem comes down to this:

class Base
{
protected:
    int m_x;
};

class Last : public Base
{
public:
    Last()
    {
        Base base;
        base.m_x = 0; // error
        m_x = 1; // no error
    }
};

The error is something like error C2248: 'Base::m_x': cannot access protected member declared in class 'Base' or error: 'int Base::m_x' is protected within this context.

The explanation is that protected is a somewhat special case. It does not only work on class level but also on the object level. And you have two relevant objects here:

  1. The Last object which is being created by the constructor, i.e. the one pointed to by this. It's also a Base object because of the is-a inheritance relationship.
  2. The local object named base within the constructor.

Now, the problem is that in the line base.m_x = 0;, you are in the context of the first object and not the second one. In other words, you are trying to access the m_x of base from outside base. C++ simply does not allow this.

A very technical explanation can be found in the C++ standard at §11.4 [class.protected], a more easily understandable one in an excellent answer here on Stack Overflow.




回答2:


protected doesn't mean quite what you think it does.

Although Last is derived from Base, member functions of Last don't have access to the protected members of any Base object - just those Base objects that are sub-objects of some Last object.

So you can write: this->Base::x because *this is a Last object, but not m_p->x, because *m_p is of static type Base.

As others have noted, I think this is actually an XY problem. Having an object which derives from two classes, and then also has a pointer to another object of one of those classes is very strange indeed. I think you need to clarify what you are trying to do.



来源:https://stackoverflow.com/questions/40440337/cant-access-protected-member-variables-of-the-most-base-class-through-stduniq

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!