private friend operator<<

自作多情 提交于 2019-12-14 03:42:40

问题


So I have a class for which I want to overload operator<< to be able to output its internal data to output stream. I want to do this only for debugging purposes and thus somehow completely hide the operator<< from outside world, so that it will be accessible only from within the *.cpp file where the implementation of my class resides. To give the operator<< access to member variables from my class I have to make it a friend of it. However declaring operator<< friend in the class enables anyone from outside world to call operator<< on this class ...

I know that I could make a regular private member function to do this, but I already have some debugging macros that make use of operator<<, so I was wondering if it was possible to get this done somehow.


回答1:


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) .




回答2:


#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_ );
  ...
};

In Windows DLL: do not export the friend function.

In gcc: hide it with __attribute__ ((visibility ("hidden")))

In this way your library user could not link this function.




回答3:


If it's possible for one translation unit to get access, it's possible for any translation unit to get access, unless you do something sneaky and unportable with #ifdef.

But you can make it hard to use by accident:

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

// ...



回答4:


Declare the operator<< as a friend in the class, but define it as a static in the file where you want it to be usable.

This does have one minor shortcoming: attempting to use it outside the file where you defined it will only result in a linker error rather than the compiler error you'd really prefer. On the other hand, that's still a lot better than no protection at all.

Here's a quick demo. First, the header with the class definition, and declaring a function junk we'll use to test access to the operator:

// trash.h
#include <iostream>

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

void junk(X const &);

Then the file where we define X and the operator, so we should be able to access the operator from here:

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

Then the second file that should not have access to the operator:

#include "trash.h"

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

Note that in this case, we cannot use an anonymous namespace instead of a file-level static function -- if you try, it'll show up as an ambiguous overload of operator<<, even for the case we want to allow.




回答5:


OK, so after reading all your answers and scratching my head for a long time, I came up with following thing. I use private inheritance to store all the data of my class and I make the output function friend of my private base class. Also to disable user from instantiating this base class, I had to make it abstract. I do not claim that this is a good software engineering and this approach is also a little too involved to my taste, as a proof of concept it stands. I compiled this with gcc 4.7.2 with the following switches: -std=c++98 -Wall -Wextra -pedantic -g

In header file 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

In implementation file 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;
}

In the some other file:

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


来源:https://stackoverflow.com/questions/15648705/private-friend-operator

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!