私人朋友运营商<<

private friend operator<<

本文关键字:lt 运营商 朋友      更新时间:2023-10-16

所以我有一个类,我想重载operator<<以便能够输出将其内部数据输出流。我想这样做只是为了调试从而以某种方式向外界完全隐藏CCD_ 2,使得它将仅从CCD_ 3文件内可访问我的类的实现存在。向成员授予operator<<访问权限类中的变量,我必须使它成为它的朋友operator<<班上的朋友让外界的任何人都可以打电话这节课上的operator<<。。。

我知道我可以做一个常规的私人会员功能来做这件事,但我已经有了一些使用CCD_ 7的调试宏,所以我想知道是否有可能以某种方式完成这项工作。

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

代码:

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(。

#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"))) 隐藏

通过这种方式,库用户无法链接此函数。

如果一个翻译单元可以访问,那么任何翻译单元都可以访问,除非你用#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<<;
// ...

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

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

好吧,所以在阅读了你们所有的答案并挠头了很长时间之后,我想出了下面的办法。我使用私有继承来存储类的所有数据,并使输出函数成为私有基类的友元。另外,为了禁止用户实例化这个基类,我不得不使它抽象化。我并不认为这是一个好的软件工程,而且这种方法也有点过于复杂,不符合我的口味,作为概念的证明。我用以下开关用gcc 4.7.2编译了这个:-std=c++98-墙-Wextra-迂腐-g

在头文件类.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;
}