Here is my situation (a simple example).
Say I have a class called HiddenData
Then I have another class called StoreHiddenData
And finaly a class called OperateHiddenData
Here's the thing, my class called HiddenData
has private members that I don't want to be visible to the rest of the world, but I still need classes to be able to operate on private members of this HiddenData
class (StoreHiddenData
and OperateHiddenData
for example). I would like to be able to pass this object around to different classes that do different things on it.
Another thing I was thinking of is to create a form of handle to these HiddenData
objects, but I can't think of a way to use handles without using friend classes again (as to give the other classes access to a table that contains what the handles point to, without exposing it to the rest of the world as well).
I was reading this question on this site just a while ago as I was researching the use of friend classes, and it has me thinking if using friends is the best way to go.
If anyone has any suggestions for how to accomplish this task without the use of friend classes in C++, or if the use of friend classes is an OK OOP practice, please provide me with any suggestions. Thanks.
EDIT: I figure I should add a little bit more information about my problem. For one thing, I am trying to create an API that consists of several classes that hide their functionality from the outside and only allow public functions to be accessed. On the inside I have data structures (or classes, doesn't really matter), that need to be passed around to these different classes with public functions. I don't want the outside to have access to anything inside these data structures other than being able to pass them around to functions of the API. I kind of think of it like the Windows API, where you pass around Handles to API functions, but you can't actually dissect the handles in your own code. Well I would like to do something very similar, but while remaining object oriented, and NOT passing these private data structures around by value because they will be BIG. Thanks again.
5 Answers 5
I would be extremely cautious about the use of friends. Although friends, have their time and place, most of the time when people use them, it's only as a convenient way of breaking encapsulation, which makes the code easy to write and deliver v1.0 but as the number of classes that operates on your HiddenData increases and as the average size of those classes increases, it will become a nightmare to read and modify.
I've used friends a few times, but only when alternatives would've been even uglier/harder to maintain. Iterators which are associated with a specific collection is definitely a good example of using friends.
Another example is implementations of finite state machines(FSM), where each state is represented by a separate class. Here your FSM is your real class, the states are just a way of splitting up logic into manageable units, but generally the data still belongs to FSM.
Bottom line, I would lean towards friends if second class is extremely small and won't be extended with more and more functionality with each release. Also the placement of the two classes should be as close as possible to one another. In case of STL's iterators, those classes are defined right inside their own container classes. Don't get any closer than that.
There are several ways of implementing your scenario while not having to break encapsulation by using friends. Here's only few of many alternatives:
Have HiddenData be truly hidden. Don't freely give out reference to it. Instead have another adapter/view/decorator (whatever you want to call it) class between the data and the public. This other class would ensure that only public operations are allowed. Then your special manipulator classes would access normal public interface of HiddenData directly.
Take a look at Visitor Pattern. It might be exactly what you need. It give you the ability to add functionality to a class without actually touching the class itself. So to implement another special manipulator which has access to internal data, you would create a new visitor class and give a reference to a single Accept() method of the HiddenData.
-
9friend don't break encapsulation for a very simple reason: a class cannot become friend of another without the other wants it. The friends, together, are just another bigger "capsule".Emilio Garavaglia– Emilio Garavaglia01/30/2012 08:40:09Commented Jan 30, 2012 at 8:40
-
1@EmilioGaravaglia: friend is a way to break encapsulation "locally". If I write an application which contains 20 classes and all data is private, but these class all happen to be friends with each other. Would you consider encapsulation to be preserved? Maybe all data is private, but I can tell you right now that app will be a royal PITA to maintain, just like one which has no encapsulation. So if you have a larger app with 500 classes, but 20 of them happen to be friends, as those 20 grow and morph and get touched by multiple developers, you will experience exactly the same painsDXM– DXM01/30/2012 09:11:29Commented Jan 30, 2012 at 9:11
-
@DXM: "local" encapsulation is an artefact of the fact, that in OOP class triples as a data type, unit of code reuse and unit of encapsulation, but it does not always make sense. When it does not, you define the unit of encapsulation with friends. If you have 20 classes that are mutual friends, you have the same problem as if you had one class with the same amount of code. It is simply bad design and does not say anything about
friend
being evil.Jan Hudec– Jan Hudec01/30/2012 11:20:43Commented Jan 30, 2012 at 11:20 -
1@DXM: C++ is a "pragmatic language". And "pragmatism" is not for Talibans. If you have 20 classes all each other friend and you know what have you done, your mess it's up to you. But you are always granted that I -using your classes with my own classes- will never be able to mess up your "private parts". Whatever mess they can be / have themseves. The "encapsulation", in a pragmatic language, is no more than that. Be sure nothing external to a context can mess up things inside. How wide a context should be, and how many things should contain is a matter of design, not of keywords.Emilio Garavaglia– Emilio Garavaglia01/30/2012 18:28:28Commented Jan 30, 2012 at 18:28
-
1As an aside, iterators in C++ are not actually members of their container anymore for quite some time now. The reason is simple: Not all the containers template-arguments are relevant to the iterator used, thus the earlier design lead to needless restrictions and duplication.Deduplicator– Deduplicator10/07/2015 14:27:28Commented Oct 7, 2015 at 14:27
C++ friends exist just for that. Every other surrogate will simply mimic the same concept.
- There are thing that are for everyone,
- Things for you only, (eventually inherited from your children)
- Things shared by you with a well define and limited set of people.
Those "people" are called "friend". Eliminating that concept will require those things to become public, or to become private, but with public accessors to be managed. You must also expect anyone to be capable of doing things with them out of your control.
No, you should not use friend classes. Expose public API that so that your clients (at this point, you, via StoreHiddenData
and OperateHiddenData
) are able to modify the contents of Hidden Data
with the use of an API. As a last resort, use getters and setters.Make sure you design your public interface to be cohesive, don't just expose functionality to be able to write fast code.
But before you do anything, read Effective C++ by Scott Meyers. It will answer a lot of your questions, and it's always useful as a reference book.
I think this is a difficult question, because it can be a matter of preference. How many friends is too many?
As a single anecdote, this is done in the wild, in large professional software codebases.
Unreal Engine
Unreal Engine, a rather large game engine written in C++ makes use of friend classes to allow protected member access for related logic.
In one such case a class that controls movement (UChaosVehicleMovementComponent
) is a friend of a manager class that facilitates physics calculations for instances of UChaosVehicleMovementComponent
This allows access to protected member functions from the manager class.
class CHAOSVEHICLES_API UChaosVehicleMovementComponent : public UPawnMovementComponent
{
friend class FChaosVehicleManager;
// ...
protected:
FVehicleState VehicleState;
}
This allows the manager class to access an array of UChaosVehicleMovementComponent
and call protected member functions (VehicleState
)
if (Vehicles[i]->VehicleState.bSleeping)
{
SleepingCount++;
}
for the curious:
Engine\Plugins\Experimental\ChaosVehiclesPlugin\Source\ChaosVehicles\Public\ChaosVehicleMovementComponent.h
You use "friend" classes if you have two or more classes that are closely connected.
You organise things in your code so that you have individual things that are separate, and often a "class" is a reasonable boundary to keep things separate, so you can change the implementation or private items of the class without changing anything else.
But sometimes you have classes that need to work very closely together. In a single class, all your methods have access to everything in the class. And if you make one change, say to a field of the class, you may have to change other things in the class, and that is the expected thing. But sometimes you have two classes where the same is true. You expect to change them together. They cannot stand on their own feet, they need each other. That's when you make one class a "friend" of the other, and that is a situation where it is absolutely fine and the right thing to do.
(Swift has an interesting feature - "fileprivate" items. They are like private items, but can be accessed by any code in the same source file. And if you have two classes that work together closely, you'd want them in one file, right? And "private" items cannot be accessed. )
friend
in C++. For example, the Iterator design pattern can usefriend
to access private members so that the aggregate (the thing being iterated over) doesn't have to expose methods for the sole purpose of iteration. It lets you keep iteration methods in the iterator and data structure maintenance methods in the data structure. Usingfriend
to accomplish similar goals is probably a good use of the language constructs of C++.