【问题标题】:Accessing private class in operator<< in namespace访问命名空间中 operator<< 中的私有类
【发布时间】:2009-08-21 09:24:11
【问题描述】:

我有一个带有私有内部类 CBar 的 CFoo 类。我想为 CFoo 实现一个流输出运算符,它又在其实现中使用 CBar 的流输出。当 CFoo 在公共命名空间中时,我可以让它工作,但是当我将它放在新的命名空间(命名空间 foobar)中时,操作员不能再访问私有内部类。我怀疑这与运算符的完整签名有关,但我无法找出指定朋友声明和实际运算符声明的正确方法,以便编译实现。谁能建议我可能会错过什么? 请注意,如果流实现在标头中内联完成,它编译,但我讨厌不必要地公开这样的实现!

在 foobar.h 中(只需注释掉 usefoobarnamespace 以测试非命名空间版本):

#define usefoobarnamespace
#ifdef usefoobarnamespace
namespace foobar
{
#endif // usefoobarnamespace
    class CFoo
    {
    public:
        CFoo() {}
        ~CFoo();
        void AddBar();
    private:
        class CBar
        {
        public:
            CBar() {m_iVal = ++s_iVal;}
            int m_iVal;
            static int s_iVal;
        };

        std::vector<CBar*> m_aBars;

        friend std::ostream& operator<<(std::ostream& rcStream, CFoo& rcFoo);
        friend std::ostream& operator<<(std::ostream& rcStream, CFoo::CBar& rcBar);
    };
    std::ostream& operator<<(std::ostream& rcStream, CFoo& rcFoo);
    std::ostream& operator<<(std::ostream& rcStream, CFoo::CBar& rcBar);
#ifdef usefoobarnamespace
}
#endif // usefoobarnamespace

在 foobar.cpp 中:

#ifdef usefoobarnamespace
using namespace foobar;
#endif // usefoobarnamespace

int CFoo::CBar::s_iVal = 0;


CFoo::~CFoo()
{
    std::vector<CBar*>::iterator barIter;
    for (barIter = m_aBars.begin(); barIter != m_aBars.end(); ++barIter)
    {
        delete (*barIter);
    }
}

void CFoo::AddBar()
{
    m_aBars.push_back(new CBar());
}


std::ostream& operator<<( std::ostream& rcStream, CFoo& rcFoo )
{
    rcStream<<"CFoo(";
    std::vector<CFoo::CBar*>::iterator barIter;
    for (barIter = rcFoo.m_aBars.begin(); barIter != rcFoo.m_aBars.end(); ++barIter)
    {
        rcStream<<(*barIter);   
    }
    return rcStream<<")";
}

std::ostream& operator<<( std::ostream& rcStream, CFoo::CBar& rcBar )
{
    return rcStream<<"CBar("<<rcBar.m_iVal<<")";
}

【问题讨论】:

    标签: c++ namespaces operator-overloading inner-classes


    【解决方案1】:

    只需将.cpp文件中的代码放入命名空间即可:

    namespace foobar {
    
    // your existing code
    
    }
    

    【讨论】:

    • 谢谢这确实有效。如果我能提供帮助,我宁愿不在我的实现文件中将代码包装在这样的命名空间中,但如果我找不到其他可行的解决方案,我将只为操作员执行此操作。
    • +1 我不明白为什么人们在定义 inside 命名空间的函数/方法时会使用 using 指令。
    • @FlintZA 愿意分享这种奇特偏好的原因吗?这是使用命名空间的最简单、最干净的方式。
    • @Neil 我们使用 Whole Tomato 的(优秀)视觉辅助。它的重构工具之一允许您自动为函数定义创建实现。这个自动生成的实现签名包含命名空间,因此使用上述方法需要对代码进行更多的手动修剪。
    【解决方案2】:

    您需要将运算符定义明确地放在命名空间中。 (或使用命名空间完全限定它们)。你这样做的方式是声明一些

    namespace foobar
    {
        std::ostream& operator<<( std::ostream& rcStream, CFoo& rcFoo )
        {
            rcStream<<"CFoo(";
            std::vector<CFoo::CBar*>::iterator barIter;
            for (barIter = rcFoo.m_aBars.begin(); barIter != rcFoo.m_aBars.end(); ++barIter)
            {
                rcStream<<(*barIter);   
            }
            return rcStream<<")";
        }
    
        std::ostream& operator<<( std::ostream& rcStream, CFoo::CBar& rcBar )
        {
            return rcStream<<"CBar("<<rcBar.m_iVal<<")";
        }
    }
    

    【讨论】:

    • 我对 Niel 的回答同上 :)
    【解决方案3】:

    您的operator&lt;&lt; functions 现在位于foobar 命名空间中,因此您应该将它们定义为foobar::operator&lt;&lt;

    【讨论】:

    • 奇怪的是,这适用于 CFoo 运算符,但不适用于 CFoo::CBar 运算符。 std::ostream& foobar::operator
    • @FlintZA:奇怪,对我有用,我在回答之前编译了它(g++ 4.3.3)。你能仔细检查一下吗?顺便说一句,我在您的代码中发现了一个可能的错误,在 (*barIter) 中,您可能想要取消引用两次 (*(*barIter)) 以便调用 CBar 重载运算符。
    • @UncleZiev 咳咳 :) 在我的实际代码案例中,内部类对象实际上在映射中,所以我取消引用 .second。我在转换为更简单的测试用例时错过了这一点。至于这个解决方案没有编译,我正在用 Visual Studio 2008 编译,所以我想这是 MS 编译器的一个怪癖——不会是第一个 ;)
    【解决方案4】:

    这个问题可以通过专门为命名空间重载流操作符来解决:

    std::ostream& foobar::operator<<( std::ostream& rcStream, CFoo& rcFoo )
    {
        rcStream<<"CFoo(";
        std::vector<CFoo::CBar*>::iterator barIter;
        for (barIter = rcFoo.m_aBars.begin(); barIter != rcFoo.m_aBars.end(); ++barIter)
        {
            rcStream<<(*barIter);   
        }
        return rcStream<<")";
    }
    
    std::ostream& foobar::operator<<( std::ostream& rcStream, CFoo::CBar& rcBar )
    {
        return rcStream<<"CBar("<<rcBar.m_iVal<<")";
    } 
    

    默认情况下,这些函数的全局定义会被重载。它们不是 CFoo 类的朋友,不能访问其私有成员。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-01-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-29
      • 1970-01-01
      • 2010-11-22
      • 1970-01-01
      相关资源
      最近更新 更多