问题
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