fbpx

C++: Break OO Privacy

Bjarne-stroustrup
 

Show how to access private or protected members of a class in an object-oriented language from outside an instance of the class, without calling non-private or non-protected members of the class as a proxy. The intent is to show how a debugger, serializer, or other meta-programming tool might access information that is barred by normal access methods to the object but can nevertheless be accessed from within the language by some provided escape hatch or reflection mechanism. The intent is specifically not to demonstrate heroic measures such as peeking and poking raw memory.

Note that cheating on your type system is almost universally regarded as unidiomatic at best, and poor programming practice at worst. Nonetheless, if your language intentionally maintains a double-standard for OO privacy, here’s where you can show it off.

C++ has the ‘friend’ keyword to indicate that one class should have access to the private data of another. Here’s a simple use case. (Please note that this code is not thread-safe.)

#include <iostream>

class CWidget; // Forward-declare that we have a class named CWidget.

class CFactory
{
	friend class CWidget;
private:
	unsigned int m_uiCount;
public:
	CFactory();
	~CFactory();
	CWidget* GetWidget();
};

class CWidget
{
private:
	CFactory& m_parent;

private:
	CWidget(); // Disallow the default constructor.
	CWidget(const CWidget&); // Disallow the copy constructor
	CWidget& operator=(const CWidget&); // Disallow the assignment operator.
public:
	CWidget(CFactory& parent);
	~CWidget();
};

// CFactory constructors and destructors. Very simple things.
CFactory::CFactory() : m_uiCount(0) {}
CFactory::~CFactory() {}

// CFactory method which creates CWidgets.
CWidget* CFactory::GetWidget()
{
	// Create a new CWidget, tell it we're its parent.
	return new CWidget(*this);
}

// CWidget constructor
CWidget::CWidget(CFactory& parent) : m_parent(parent)
{
	++m_parent.m_uiCount;

	std::cout << "Widget spawning. There are now " << m_parent.m_uiCount << " Widgets instanciated." << std::endl;
}

CWidget::~CWidget()
{
	--m_parent.m_uiCount;

	std::cout << "Widget dieing. There are now " << m_parent.m_uiCount << " Widgets instanciated." << std::endl;
}

int main()
{
	CFactory factory;

	CWidget* pWidget1 = factory.GetWidget();
	CWidget* pWidget2 = factory.GetWidget();
	delete pWidget1;

	CWidget* pWidget3 = factory.GetWidget();
	delete pWidget3;
	delete pWidget2;
}boost/format.hpp>
#include <boost/foreach.hpp>
#include <iostream>
#include <math.h>
using std::string;
using namespace boost::assign;

int get_Index(float angle)
{
	return static_cast<int>(floor(angle / 11.25 +0.5 )) % 32 + 1;
}

string get_Abbr_From_Index(int i)
{
	static boost::array<std::string, 32> points(list_of
	("N")("NbE")("NNE")("NEbN")("NE")("NEbE")("ENE")("EbN")
	("E")("EbS")("ESE")("SEbE")("SE")("SEbS")("SSE")("SbE")
	("S")("SbW")("SSW")("SWbS")("SW")("SWbW")("WSW")("WbS")
	("W")("WbN")("WNW")("NWbW")("NW")("NWbN")("NNW")("NbW"));
	return points[i-1];
}

string Build_Name_From_Abbreviation(string a)
{
	string retval;
	for (int i = 0; i < a.size(); ++i){
		if ((1 == i) && (a[i] != 'b') && (a.size() == 3)) retval += "-";
		switch (a[i]){
		case 'N' : retval += "north"; break; 
		case 'S' : retval += "south"; break; 
		case 'E' : retval += "east";  break; 
		case 'W' : retval += "west";  break; 
		case 'b' : retval += " by ";  break;
		}
	}
	retval[0] = toupper(retval[0]);
	return retval;
}

int main()
{
	boost::array<float,33> headings(list_of
	(0.0)(16.87)(16.88)(33.75)(50.62)(50.63)(67.5)(84.37)(84.38)(101.25)
	(118.12)(118.13)(135.0)(151.87)(151.88)(168.75)(185.62)(185.63)(202.5)
	(219.37)(219.38)(236.25)(253.12)(253.13)(270.0)(286.87)(286.88)(303.75)
	(320.62)(320.63)(337.5)(354.37)(354.38));
	int i;
	boost::format f("%1$4d %2$-20s %3$_7.2f");

	BOOST_FOREACH(float a, headings)
	{
		i = get_Index(a);
		std::cout << f % i %  Build_Name_From_Abbreviation(get_Abbr_From_Index(i)) % a << std::endl;
	}
	return 0;
}
Output:
Widget spawning. There are now 1 Widgets instanciated.
Widget spawning. There are now 2 Widgets instanciated.
Widget dieing. There are now 1 Widgets instanciated.
Widget spawning. There are now 2 Widgets instanciated.
Widget dieing. There are now 1 Widgets instanciated.
Widget dieing. There are now 0 Widgets instanciated.

Without the “friend” mechanism, it’s still possible to meaningfully modify any member in another class, as long as you know that member’s address in memory, and its type. Here’s the same program as above, but using a pointer to m_uicount, rather a reference to the factory:

#include <iostream>

class CWidget; // Forward-declare that we have a class named CWidget.

class CFactory
{
private:
	unsigned int m_uiCount;
public:
	CFactory();
	~CFactory();
	CWidget* GetWidget();
};

class CWidget
{
private:
	unsigned int* m_pCounter;

private:
	CWidget(); // Disallow the default constructor.
	CWidget(const CWidget&); // Disallow the copy constructor
	CWidget& operator=(const CWidget&); // Disallow the assignment operator.
public:
	CWidget(unsigned int* pCounter);
	~CWidget();
};

// CFactory constructors and destructors. Very simple things.
CFactory::CFactory() : m_uiCount(0) {}
CFactory::~CFactory() {}

// CFactory method which creates CWidgets.
CWidget* CFactory::GetWidget()
{
	// Create a new CWidget, tell it we're its parent.
	return new CWidget(&m_uiCount);
}

// CWidget constructor
CWidget::CWidget(unsigned int* pCounter) : m_pCounter(pCounter)
{
	++*m_pCounter;

	std::cout << "Widget spawning. There are now " << *m_pCounter<< " Widgets instanciated." << std::endl;
}

CWidget::~CWidget()
{
	--*m_pCounter;

	std::cout << "Widget dieing. There are now " << *m_pCounter<< " Widgets instanciated." << std::endl;
}

int main()
{
	CFactory factory;

	CWidget* pWidget1 = factory.GetWidget();
	CWidget* pWidget2 = factory.GetWidget();
	delete pWidget1;

	CWidget* pWidget3 = factory.GetWidget();
	delete pWidget3;
	delete pWidget2;
}

SOURCE

Content is available under GNU Free Documentation License 1.2.