2

所以我有一个类,我想对其进行重载operator<<,以便能够将其内部数据输出到输出流。我只想出于调试目的这样做,因此以某种方式完全隐藏了operator<<外界,因此只能从*.cpp我的类的实现所在的文件中访问它。要从operator<<我的类中访问成员变量,我必须使它成为它的朋友。然而,在班级中声明 operator<<朋友可以让任何来自外部世界的人都可以调用 operator<<这个班级......

我知道我可以创建一个常规的私有成员函数来执行此操作,但我已经有一些使用的调试宏operator<<,所以我想知道是否有可能以某种方式完成此操作。

4

5 回答 5

2

You can move the operator<< functionality to a helper proxy class. When the proxy is used as the RHS of <<, then the original object is printed. Define a private implicit conversion from the original to the proxy. Now anyone has access to operator<<, but only the class has the ability to construct the proxy.

Code:

class private_printable {
    int state;

    struct proxy {
        private_printable const &r;
    };
    operator proxy () const { return { * this }; }

    friend std::ostream & operator << ( std::ostream & s, proxy const & o )
        { return s << o.r.state; }

public:
    private_printable() : state( 5 ) {}
    void debug() { std::cout << * this << '\n'; }
};

Note that the proxy doesn't need to be friended. The only change from the normal way of doing things is that the proxy and conversion function exist. The friend operator<< is found by argument-dependent lookup, without a namespace-scope declaration, even though it doesn't take a private_printable argument. Then the conversion makes it viable. Don't think a cleaner solution is possible :v) .

于 2013-03-26T23:50:58.463 回答
0

如果一个翻译单元可以访问,那么任何翻译单元都可以访问,除非您使用#ifdef.

但是您可能会不小心使其难以使用:

// example.hpp
#ifndef EXAMPLE_CLASS_HPP
#define EXAMPLE_CLASS_HPP

#include <ostream>

class Example;

namespace Example_debug {
    std::ostream& operator<<(std::ostream&, const Example&);
}

class Example {
public:
    // ...
private:
    void debug_print(std::ostream&) const;
    friend std::ostream& Example_debug::operator<<(
      std::ostream&, const Example&);
};

#endif

// example.cpp
#include "example.hpp"

std::ostream& Example_debug::operator<<(std::ostream& os, const Example& obj) {
    obj.debug_print(os);
    return os;
}

using Example_debug::operator<<;

// ...
于 2013-03-26T22:56:35.987 回答
0
#ifdef WIN32
# define DLL_LOCAL
# define DLL_PUBLIC __declspec(dllexport)
#else
# define DLL_LOCAL __attribute__ ((visibility ("hidden")))
# define DLL_PUBLIC
#endif

class DLL_PUBLIC Example
{
  DLL_LOCAL friend std::ostream& operator << ( std::ostream& os_, const Example& inst_ );
  ...
};

在 Windows DLL 中:不导出友元函数。

在 gcc 中:隐藏它__attribute__ ((visibility ("hidden")))

这样,您的图书馆用户无法链接此功能。

于 2013-03-26T22:39:13.460 回答
0

将 声明operator<<为类中的朋友,但将其定义static文件中您希望它可用的位置。

这确实有一个小缺点:尝试在您定义它的文件之外使用它只会导致链接器错误,而不是您真正喜欢的编译器错误。另一方面,这仍然比没有保护要好得多

这是一个快速演示。首先,带有类定义的头文件,并声明一个junk我们将用来测试对操作符的访问的函数:

// trash.h
#include <iostream>

class X { 
    friend std::ostream &operator<<(std::ostream &, X const &);
};

void junk(X const &);

然后是我们定义的文件X和操作符,所以我们应该可以从这里访问操作符:

#include "trash.h"

static std::ostream &operator<<(std::ostream &os, X const &x) {
    return os << "x";
}

int main() {
    X x;
    std::cout << x;
    junk(x);
    return 0;
}

然后是不应该访问操作员的第二个文件:

#include "trash.h"

void junk(X const &x) {
    // un-comment the following, and the file won't link:
    //std::cout << x; 
}

请注意,在这种情况下,我们不能使用匿名命名空间来代替文件级static函数——如果您尝试,它会显示为 的模棱两可的重载operator<<,即使在我们想要允许的情况下也是如此。

于 2013-03-26T23:16:57.353 回答
0

好的,所以在阅读了你所有的答案并挠了很长时间之后,我想出了以下事情。我使用私有继承来存储我的类的所有数据,并让我的私有基类的输出函数成为朋友。另外,为了禁止用户实例化这个基类,我必须把它抽象化。我并不是说这是一个好的软件工程,而且这种方法对我的口味也有点过于复杂,作为它所代表的概念证明。我用 gcc 4.7.2 用以下开关编译了这个: -std=c++98 -Wall -Wextra -pedantic -g

在头文件 class.h 中:

#ifndef CCLASS_H
#define CCLASS_H

#include <iostream>

class CDataBase
{
  protected:
    /// all data members will go here
    int m_data;

    CDataBase(int data = 0) : m_data(data) { }

    /**
     * Make the base virtual, so that it cannot be instantiated
     */
    virtual ~CDataBase(void) = 0;

    /// and this function is a friend of only the base class
    friend std::ostream & operator<<(std::ostream & os, const CDataBase & base);
};

class CMyClass : private CDataBase
{
  public:
    CMyClass(void) : CDataBase(42) { }
    virtual ~CMyClass(void) { }
    void test(void);
};

#endif

在实现文件 Class.cpp

#include "CClass.h"


std::ostream & operator<<(std::ostream & os, const CDataBase & base)
{
  os << base.m_data;
  return os;
}

CDataBase::~CDataBase(void)
{
}

void CMyClass::test(void)
{
  std::cout << *this << std::endl;
}

在其他一些文件中:

#include "CClass.h"

#include <iostream>

int main(void)
{
  CMyClass cls;
  cls.test();  // this works

  // this failes, because CDataBase is abstract
  //CDataBase base;

  // this fails as well, because CDataBase is inaccessible
  //std::cout << cls << std::endl;
  return 0;
}
于 2013-03-26T23:55:58.837 回答