私人朋友运营商<<

时间:2013-03-26 22:35:59

标签: c++

所以我有一个我希望重载operator<<以便能够输出的类 其内部数据输出流。我想这只是为了调试 目的,从某种程度上完全隐藏operator<<来自外部世界, 这样它只能从*.cpp文件中访问 我班的实施所在。授予operator<<成员访问权限 来自我班级的变量我必须把它变成它的朋友。无论如何宣告 班上的operator<<朋友可以让外面的任何人打电话 这个班级operator<< ......

我知道我可以制作一个普通的私人会员功能来做到这一点, 但我已经有一些使用operator<<的调试宏, 所以我想知道是否有可能以某种方式完成这项工作。

5 个答案:

答案 0 :(得分:2)

您可以将operator<<功能移动到帮助程序代理类。当代理用作<<的RHS时,则打印原始对象。定义从原始代理到代理的private隐式转换。现在任何人都可以访问operator<<,但只有该类能够构建代理。

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'; }
};

请注意,代理不需要为其提供支持。正常做事方式的唯一变化是存在代理和转换功能。 friend operator<<是通过参数依赖查找找到的,没有命名空间范围声明,即使它不采用private_printable参数。然后转换使其可行。不要认为更清洁的解决方案是可能的:v)。

答案 1 :(得分: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")))

隐藏它

通过这种方式,您的库用户无法链接此功能。

答案 2 :(得分: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<<;

// ...

答案 3 :(得分: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<<的模糊重载,甚至对于我们想要允许的情况。

答案 4 :(得分: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;
}