【问题标题】:Nested Class member function can't access function of enclosing class. Why?嵌套类成员函数不能访问封闭类的函数。为什么?
【发布时间】:2010-06-17 01:07:59
【问题描述】:

请看下面的示例代码:

class A
{
private:
    class B
    {
    public:
        foobar();
    };
public:
    foo();
    bar();
};

在 A 类和 B 类实现中:

A::foo()
{
    //do something
}

A::bar()
{
    //some code
    foo();
    //more code
}

A::B::foobar()
{
    //some code
    foo(); //<<compiler doesn't like this
}

编译器在方法 foobar() 中标记对 foo() 的调用。早些时候,我将 foo() 作为类 A 的私有成员函数,但假设 B 的函数看不到它,则更改为 public。当然,它没有帮助。我正在尝试重用 A 的方法提供的功能。为什么编译器不允许这个函数调用?正如我所看到的,它们是同一个封闭类(A)的一部分。我认为在 C++ 标准中封闭类的嵌套类成员的可访问性问题已经解决。

如何在不为 B 重写相同的方法 (foo()) 的情况下实现我想要做的事情,从而使 B 嵌套在 A 中?

我正在使用 VC++ 编译器版本 9 (Visual Studio 2008)。感谢您的帮助。

【问题讨论】:

    标签: c++ nested-class


    【解决方案1】:

    foo()A 的非静态成员函数,您试图在没有实例的情况下调用它。
    嵌套类B 是一个单独的类,它只有一些访问权限,对A 的现有实例没有任何特殊知识。

    如果B 需要访问A,你必须给它一个引用,例如:

    class A {
        class B {
            A& parent_;
        public:
            B(A& parent) : parent_(parent) {}
            void foobar() { parent_.foo(); }
        };
        B b_;
    public:
        A() : b_(*this) {}
    };
    

    【讨论】:

    • +1,只是一个挑剔 - parent 可能不是这里成员变量的最佳名称 - 容易与继承混淆。
    • 我只想提一下,嵌套类 B 有一个很好的设计理由来引用类 A。
    • 感谢您举例说明。 C++ 标准 11.8,我猜它已经改变了,它讨论了嵌套类的类成员访问。我知道 gcc 允许通过嵌套类访问(我很确​​定),但 MS VC 编译器不允许。嗯……很有趣。
    • @Rahul:是的,你有访问权限,但是实例之间没有隐含的关系——它只是一个你仍然需要显式创建实例的类。
    • @Nikolai:嗯,我觉得很清楚,因为它明确了所有权关系,并且继承上下文中的父级通常标记为 base。至少我无法想象在通用示例中有更好的名称 - 有什么建议吗?
    【解决方案2】:

    这是一个自动魔术,尽管可能是不可移植的技巧(尽管从 6.0 开始就在 VC++ 上工作)。 B 类必须是 A 类的成员才能正常工作。

    #ifndef OUTERCLASS
    #define OUTERCLASS(className, memberName) \
        reinterpret_cast<className*>(reinterpret_cast<unsigned char*>(this) - offsetof(className, memberName))
    #endif 
    
    class A
    {
    private:
        class B
        {
        public:
            void foobar() {
               A* pA = OUTERCLASS(A, m_classB);
               pA->foo();
            }
        } m_classB;
    public:
        foo();
        bar();
    };
    

    【讨论】:

    • 感谢 Igor 回复我的问题并提供示例。您和 Georg 的实现几乎相似,都引用了外部类,但是,我选择了 Georg 的响应,因为它更简洁。
    • 别担心,这毕竟是一个看起来很讨厌的黑客。话虽如此,它非常可靠,而且很有趣:)
    【解决方案3】:

    基本上是 Georg Fritzsche 所说的

    #include <iostream>
    #include <cstring>
    using namespace std;
    
    class A
    {
    private:
        class B
        {
         A& parent_;
         public:
            //B();  //uncommenting gives error
            ~B();
            B(A& parent) : parent_(parent) {}
    
            void foobar() 
            { 
             parent_.foo();  
             cout << "A::B::foo()" <<endl; 
            }
    
            const std::string& foobarstring(const std::string& test) const 
            { 
             parent_.foostring(test); cout << "A::B::foostring()" <<endl;
            }
        };
    public:
        void foo();
        void bar();
        const std::string& foostring(const std::string& test) const;
        A(); 
        ~A(){};
        B b_;
    };
    
    //A::B::B() {}; //uncommenting gives error
    A::B::~B(){};
    
    A::A():b_(*this) {}
    
    
    void A::foo()
    {
        cout << "A::foo()" <<endl;
    }
    
    const std::string& A::foostring(const std::string& test) const
    {
        cout << test <<endl;
        return test;
    }
    
    void A::bar()
    {
        //some code
        cout << "A::bar()" <<endl;
        foo();
        //more code
    }
    
    int main(int argc, char* argv[])
    {
    A a;
    a.b_.foobar();
    a.b_.foobarstring("hello");
    
    return 0;
    }
    

    如果你取消注释默认的 B 构造函数,你会得到一个错误

    【讨论】:

    • @cbinder 也尝试取消注释A::B::B() {};
    • 如果你想使用 B() 类构造函数,那么你可以使用 reinterpret_cast 。你可以参考这个:stackoverflow.com/a/68117065/9524565
    【解决方案4】:

    如果你想重用 A 的功能,那么你应该从 A 继承而不是在其中嵌套 B。

    【讨论】:

    • 我没有在 B 中扩展 A 的功能,所以,没有必要从 A 继承 B。另外,我想让 B 对 A 类的用户隐藏。
    【解决方案5】:

    结合 Igor Zevaka 和热心极客的答案。另外,使用 reinterpret_cast 计算偏移量(如果使用 new 关键字创建类成员变量):

    #include <iostream>
    #include <cstring>
    using namespace std;
    
    template < typename T, typename U > constexpr size_t offsetOf(U T:: *member)
    {
        return (char*) &((T*) nullptr->*member) - (char*) nullptr;
    }
    
    class A
    {
        private:
            class B
            {
             public:
                B(string message);
                ~B();
    
                void foobar()
                {
                    A *pA = reinterpret_cast<A*> (reinterpret_cast< unsigned char*> (this) - offsetOf(&A::b_));
                    pA->foo();
                    pA->bar();
                    std::cout << "DONE!";
                }
            };
        public:
            void foo();
            void bar();
            A();
            ~A() {};
            B* b_ = new B("Hello World!");
    };
    
    A::A() 
    {
        cout << "A constructor\n";
    };
    A::B::B(string message) {
        cout << "B constructor\n";
        cout << "Message =  " << message << "\n";
    };
    A::B::~B() {};
    
    void A::foo()
    {
        cout << "A::foo()" << endl;
    }
    
    void A::bar()
    {
        cout << "A::bar()" << endl;
        foo();
    }
    
    int main(int argc, char *argv[])
    {
        A* a = new A();
        a->b_->foobar();
    
        return 0;
    }
    

    输出:

    B constructor
    Message =  Hello World!
    A constructor
    A::foo()
    A::bar()
    A::foo()
    DONE!
    

    参考资料:

    https://stackoverflow.com/a/10607424/9524565

    https://stackoverflow.com/a/3058382/9524565

    https://stackoverflow.com/a/20141143/9524565

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-16
      • 2016-11-22
      • 2010-12-12
      • 2021-11-01
      相关资源
      最近更新 更多