【问题标题】:Redefinition of the operator <<in a singleton在单例中重新定义运算符 <<
【发布时间】:2011-11-10 18:55:13
【问题描述】:

我有一个 Linux 上的 C++ 类,它允许我在控制台上显示消息,具体取决于应用程序的运行位置。如果它在我的计算机上运行,​​则消息以控制台模式显示。否则,所有内容都会记录在文本文件中以供以后查看。我创建了一个外部对象来使用它。我想尝试使这个类成为单例,但没有成功。我有这个错误似乎是当我来编译我的程序时:

错误:'Log *'和'const char [12]'类型的无效操作数到二进制'operator

还有:

错误:'logOutput' 中的'operator

我想听听你的意见。提前感谢您的关注。

我目前的功能类

typedef std::vector<unsigned char> Buffer_T;

class Log
{
public:
    Log();
    virtual ~Log();

    void init()
    {
    #indef FriendlyArm
        output = new ofstream("/home/arm/Log.txt");
    #else
        output = &cout;
    #endif
    }


    template<typename T>
    Log operator<<( T const& value )
    {
        (*output) << value;
        return *this;
    }

    Log& operator<<( std::ostream&(*f)(std::ostream&) )
    {
        (*output) << f;
        return *this;
    }

    Log& operator<<(Buffer_T& Buf)
    {
        if( Buf.size() > 0 )
        {
            for( unsigned int i = 0; i < Buf.size(); i++ )
            {
                if( Buf[i] >= 32 && Buf[i] <= 127 )
                {
                    (*output) << Buf[i];
                }
                else
                {
                    (*output) << "0x" << std::setfill( '0' ) << std::hex << std::setw( 2 ) << unsigned( Buf[i] );
                }
            }
        }
        return *this;
    }


private:
    ostream *output;
};
#endif /* LOG_H_ */

这是我单身的尝试

typedef std::vector<unsigned char> Buffer_T;

class Log
{
    public:
        static Log *createOrGet()
        {
            if(_unique == NULL)
            {
                _unique = new Log();
            }

            return _unique;
        }

        static void kill()
        {
            if(_unique != NULL)
            {
                delete _unique;
                _unique = NULL;
            }
        }

        void init()
        {
        #indef FriendlyArm
            output = new ofstream("/home/arm/Log.txt");
        #else
            output = &cout;
        #endif
        }

        template<typename T>
        Log operator<<( T const& value )
        {
            (*output) << value;
            return *this;
        }

        Log& operator<<( std::ostream&(*f)(std::ostream&) )
        {
            (*output) << f;
            return *this;
        }

        Log& operator<<(Buffer_T& Buf)
        {
            if( Buf.size() > 0 )
            {
                for( unsigned int i = 0; i < Buf.size(); i++ )
                {
                    if( Buf[i] >= 32 && Buf[i] <= 127 )
                    {
                         (*output) << Buf[i];
                    }
                    else
                    {
                        (*output) << "0x" << std::setfill( '0' ) << std::hex << std::setw( 2 ) << unsigned( Buf[i] );
                    }
                }
            }
            return *this;
        }

    private:
        Log();
        virtual ~Log();
        static Log *_unique;
        ostream *output;
};
Log *Log::_unique = NULL;
#endif

【问题讨论】:

  • @John 他们大多数时候并不理想,但总是让某人抱怨他们有多讨厌他们,这很烦人。你知道,单例可以很有用;只需检查一些 Boost 库的实现即可。
  • @Paul:我不是在和你说话。 OP 可能没有意识到单例通常是一种糟糕的设计技术。
  • @John 你不是在跟我说话吗?!你几岁?!
  • @Paul:我只是想指出我不是在“窃听”。我在“窃听” OP。
  • @Jean:请发布复制此问题的测试代码。

标签: c++ linux pointers singleton operator-overloading


【解决方案1】:

重要提示:不要误会,但你会犯一些错误,表明你不是很有经验,而且单例很难正确使用。在记录器的情况下,它们没问题,只要确保始终将它们用作最后​​的手段。

我没有时间讨论为什么 operator&lt;&lt; 不适合你,但这里有一些关于如何改善单身人士的提示(你似乎也在寻求建议)。

实例函数

// The idiomatic way is to call this function 'Instance',
// but this doesn't really matter.
static Log& Instance() {
    static Log obj;
    return obj;
}

这就是 Meyer 的单例。对于单线程应用程序来说,这很棒:在第一次调用函数时创建实例,并在应用程序关闭时自动销毁(您不需要kill 函数)。

你的私有虚拟析构函数

我注意到你有一个私有的虚拟析构函数。当类中至少有一个其他虚函数时,您只需要一个虚析构函数。不是这种情况;使析构函数成为非虚拟的。

强制执行单个实例

你将构造函数设为私有——这很好,你阻止了我直接创建单例的多个实例。但是,您并没有阻止我复制现有的单例。为了防止您还需要将 CopyConstructor 和 AssignmentOperator 设为非公开:

protected:
  Log();
  Log(const Log&); // CopyConstructor
  Log& operator=(const Log&); // AssignmentOperator
  ~Log();

(析构函数也应该是私有的,以防止我删除该类的唯一实例。)

还请注意,我将它们设为 protected,而不是 public。如果您不知道区别,请查看(此处没有足够的空间进行解释)。我将它们设为protected,因此如果需要,您可以从Log 类继承。

【讨论】:

    【解决方案2】:

    在您的 OP 的 cmets 中,您发布了以下测试工具:

    int main()
    {
        Log *logOutput; 
        logOutput = Log::createOrGet(); 
        logOutput << "ProcessCommand called";
        logOutput << Data << endl; // I dont know what Data is, so I commented this out
    }
    

    您需要取消引用 logOutput 才能流式传输到它。否则,您将流式传输到指向日志的指针,而不是指向日志本身。

    但是您也有其他问题。你的方法模板:

    template<typename T>
    Log operator<<( T const& value )
    {
        (*output) << value;
        return *this;
    }
    

    按值返回一个Log,这将导致一个临时的,当它超出范围时被销毁,但Log::~Log被声明为private,因此无法访问。

    更改您的 operator&lt;&lt;s 以通过引用返回 Log

    template<typename T>
    const Log& operator<<( T const& value ) const
    {
        (*output) << value;
        return *this;
    }
    
    Log& operator<<( std::ostream&(*f)(std::ostream&) )
    {
        (*output) << f;
        return *this;
    }
    
    Log& operator<<(Buffer_T& Buf) 
    {
        if( Buf.size() > 0 )
        {
            for( unsigned int i = 0; i < Buf.size(); i++ )
            {
                if( Buf[i] >= 32 && Buf[i] <= 127 )
                {
                        (*output) << Buf[i];
                }
                else
                {
                    (*output) << "0x" << std::setfill( '0' ) << std::hex << std::setw( 2 ) << unsigned( Buf[i] );
                }
            }
        }
        return *this;
    }
    

    【讨论】:

      【解决方案3】:

      来自您的评论:

        Log *logOutput;
        logOutput = Log::createOrGet();
        logOutput << "ProcessCommand called" << endl;
        logOutput << Data << endl;
      

      问题:您的Log::createOrGet() 返回一个Log*,而定义的operator&lt;&lt; 采用Log(注意那里缺少'*')。

      有两种解决方案:

      • 在任何地方使用*logOutput。这很麻烦,所以最好使用替代方法:
      • 将定义更改为Log&amp; logOutput; 并调整Log::createOrGet() 以返回Log&amp;(最后一点:参见Paul Manta 对Instance() 的定义)

      不要忘记阅读和理解 Paul Manta 的言论!

      注意:虽然这将解决引用的错误消息,但这不会解决您代码中的其他问题。重写代码似乎是唯一的解决方案;请参阅 Paul Manta 的回答(再次)以获取建议。

      【讨论】:

      • 问题不止这些。
      • @JohnDibling 你可能是对的,我没有在编译器中测试它。恕我直言,无论它试图实现什么,代码都太复杂了(这就是我重定向到 Paul Manta 言论的原因)。但至少我的回答解释了引用的错误消息。
      猜你喜欢
      • 2023-04-03
      • 1970-01-01
      • 2012-07-09
      • 1970-01-01
      • 2011-02-09
      • 1970-01-01
      • 1970-01-01
      • 2011-11-26
      • 1970-01-01
      相关资源
      最近更新 更多