内部类访问外部类

22

我相对于C++比较新手,我已经查找了很多关于这个问题的答案,但是我从未得到满意的答案。

假设我有一个名为FSM的结构体,在我的代码中,可能会创建多个FSM实例。其中一个FSM的属性是int X,它不是静态的,每个FSM实例都应该有自己的X值。

现在,FSM的另一个属性是另一个结构体submachine,需要像这样读取X的值:

struct FSM
{
  public:
    int x;

    int getX(){return x;}

    struct submachine
    {
        void onentry() {int g = getX();};
    };
};

以下是出现的错误:

Error: 'FSM::getX' : 非法调用非静态成员函数

我的问题是,submachineFSM 的成员,那么它不应该可以访问 FSM 的所有属性的本地实例吗?如果不行,那么当我们创建 FSM 实例时,我们不会同时创建其所有成员,即 submachine 吗?如果是这样,为什么我们需要创建一个 onentry() 需要的对象呢?

我假设编译器是正确的,那么我也想知道有没有办法使它工作。

注意:不幸的是,内部结构(submachine)的实例是在调用事件时实例化的,因此我只能定义类型,而不能在 FSM 中实例化它们的对象。

4个回答

40

我的问题是,Submachine是FSM的成员,所以它应该可以访问FSM的所有属性的本地实例,对吗?

不。与Java不同,内部类对象没有隐式引用外部对象。

我们难道不会创建其所有成员的一个实例,即submachine吗?

submachine是一个类型,而不是成员变量。如果你想要一个成员变量,你需要像这样做:

struct FSM {
    struct submachine {
        ...
    };

    submachine sm;  // Member variable of type submchine
};

如果你希望sm“看到”它的父对象,你需要显式地传递它:

struct FSM {
    struct submachine {
        FSM &parent;  // Reference to parent
        submachine(FSM &f) : parent(f) {}  // Initialise reference in constructor
    };

    submachine sm;

    FSM() : sm(*this) {}  // Pass reference to ourself when initialising sm
};
请注意,对于不是成员变量的submachine实例也适用相同的原则。如果您希望它们能够访问FSM实例,则需要传递一个引用。请注意,您可以使用指针而不是引用。实际上,在许多情况下,指针提供更大的灵活性。

1
@Kam:你需要在某个地方实例化一个submachine,但是如何实现呢?它将如何与特定的FSM对象绑定? - Oliver Charlesworth
1
@Kam:好的。同样的原则适用。每当您构造一个“submachine”实例时,您都需要向其传递对“FSM”实例的引用。(但是,我不熟悉那个Boost库,所以也许API中有一些棘手的东西。) - Oliver Charlesworth
1
@Kam:我不明白你刚才说的话。你应该提出一个新问题。 - Mooing Duck
那个技巧是将父对象的引用/指针传递给子对象,我有时看到这种习惯被称为“后指针”或“后引用”。虽然我不知道它是否是一个广泛认可的术语。 - Emile Cormier
1
我在 Boost.MSM 的状态机(和子状态机)构建文档中发现了一条注释,希望它对原帖作者更有意义,而不是对我自己。 - Useless
显示剩余8条评论

3
考虑到你的例子,我可以合法地编写一个自由函数。
void foo()
{
    FSM::submachine sub;
    sub.onentry();
}

没有FSM实例可以引用sub

可以像Oli所说的那样,让submachine对象存储其父FSM对象的引用,或者直接将x的值传递到onentry中(它被调用的方式不清楚)。


Boost.MSM文档中快速查看后,我在非默认构造的子机中找到了这个注意事项。

它很丑陋,我不太理解后端以便在此进行转述,而且孤立地使用文字代码不足以表达足够的意义。

从那里链接的示例代码还显示了子机条目方法的以下签名:

template <class Event,class FSM> void on_entry(Event const&,FSM& );

如果这是准确的,你可以在on_entry中存储指向外部状态机的指针,或者提取x的值并记录在子状态机中。


谢谢!这正是我在寻找的,现在我真的明白了。这篇帖子和OLI的帖子让我豁然开朗。我希望我能接受两个答案。 - Kam

2
请注意,声明struct submachine仅定义了类型,它并没有在该类型的类中实际创建一个字段。
您需要以下之一:
struct submachine mysub; // creates a field after the class is defined

或者

struct submachine
{
  . . .
} mysub; // creates the field "mysub" also, as the structure is being defined

这使得mysub成为字段,您可以通过与访问x相同的方式来访问它。 submachine的定义需要包括一个特定的FSM(例如指针字段FSM*),并且可能需要一个构造函数submachine(FSM* fsm): fsm_(fsm) {}来初始化它。这样,您就可以使用fsm_->getX()来访问某个X值。

不幸的是,内部结构的实例是在运行时声明的(事件),因此我只能定义类型,而不能为它们实例化对象。 - Kam
2
你仍然不能在没有“FSM”对象的情况下执行int g = getX();。这怎么回答问题呢? - Jesse Good

1

我只是猜测你想做什么,但如果我的猜测正确,你可能正在考虑类似以下的事情。

struct FSM_Base {
    int x;

    struct submachine1;
    struct submachine2;

    FSM_Base () : x(0) {}
    virtual ~FSM_Base () {}
};

struct FSM_Base::submachine1 : virtual public FSM_Base {
    void oneentry () { int g = x; }
};

struct FSM_Base::submachine2 : virtual public FSM_Base {
    void oneentry () { int g = x; }
};

struct FSM : public FSM_Base::submachine1,
             public FSM_Base::submachine2 {
    FSM_Base::submachine1 * sub1 () { return this; }
    FSM_Base::submachine2 * sub2 () { return this; }
};

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接