The friend keyword is kinda useless

The Partridge Family were neither partridges nor a family. Discuss.
Post Reply
User avatar
cyboryxmen
Posts: 190
Joined: November 14th, 2014, 2:03 am

The friend keyword is kinda useless

Post by cyboryxmen » December 10th, 2016, 4:18 am

I am using class hierarchies more as I try to code up systems. They really are a good way to represent a system of classes where a class(system) will be made up of multiple subclasses(components). Since you can pretty much make classes within classes endlessly, you can make really complex systems of classes made out of other classes made out of other classes.

Family.h

Code: Select all

#pragma once
#include <cstdlib>
#include <string>
#include <iostream>

class Family
{
public:
	class Child
	{
	public:
		Child ( Family& family, const std::string& name );

		void PrintName ( );

	private:
		std::string name_;
		Family& family_;
	};

	Family ( const std::string& family_name );

private:
	std::string family_name_;
};
Family.cpp

Code: Select all

#include "Family.h"

Family::Family ( const std::string& family_name ) : family_name_ ( family_name )
{
}

Family::Child::Child ( Family& family, const std::string& name ) : family_ ( family ), name_ ( name )
{
}

void Family::Child::PrintName ( )
{
	std::cout << name_ << ' ' << family_.family_name_;
}
main.cpp

Code: Select all

#include <cstdlib>
#include <iostream>
#include "Family.h"

int main ( void )
{
	Family family ( "Smith" );
	Family::Child child1 ( family, "Bob" );
	Family::Child child2 ( family, "Sarah" );
	Family::Child child3 ( family, "Vladmir Uthmaniyah R'lyeh" );

	std::cout << "Name: "; child1.PrintName ( ); std::cout << std::endl;
	std::cout << "Name: "; child2.PrintName ( ); std::cout << std::endl;
	std::cout << "Name: "; child3.PrintName ( ); std::cout << std::endl;

	system ( "pause" );
	return 1;
}
Unfortunately, that presents the problem that the class definition would look really messy for complex systems since the hierarchy would consist of multiple classes within classes within classes. Not to mention that changing just one of those classes would cause the entire hierarchy to be recompiled again since they are all in one definition. Most of the time, I just use the friend declaration to define the system. That way, I can define these classes in separate files and make things look neater and compile more quickly.

Family.h

Code: Select all

#pragma once
#include <cstdlib>
#include <string>

class Family
{
public:
	friend class Child;

	Family ( const std::string& family_name );

private:
	std::string family_name_;
};
Family.cpp

Code: Select all

#include "Family.h"

Family::Family ( const std::string& family_name ) : family_name_ ( family_name )
{
}
Child.h

Code: Select all

#include <cstdlib>
#include <string>
#include "Family.h"

class Child
{
public:
	Child ( Family& family, const std::string& name );

	void PrintName ( );

private:
	std::string name_;
	Family& family_;
};
Child.cpp

Code: Select all

#include "Child.h"
#include <iostream>

Child::Child ( Family& family, const std::string& name ) : family_ ( family ), name_ ( name )
{
}

void Child::PrintName ( )
{
	std::cout << name_ << ' ' << family_.family_name_;
}
Then I wondered...can't I just forward declare the class instead of defining it to create the hierarchy? Lo and behold, apparently this is something that you can actually do all along.

Family.h

Code: Select all

#pragma once
#include <cstdlib>
#include <string>

class Family
{
public:
	class Child;

	Family ( const std::string& family_name );

private:
	std::string family_name_;
};
Family.cpp

Code: Select all

#include "Family.h"

Family::Family ( const std::string& family_name ) : family_name_ ( family_name )
{
}
Child.h

Code: Select all

#include <cstdlib>
#include <string>
#include "Family.h"

class Family::Child
{
public:
	Child ( Family& family, const std::string& name );

	void PrintName ( );

private:
	std::string name_;
	Family& family_;
};
Child.cpp

Code: Select all

#include "Child.h"
#include <iostream>

Family::Child::Child ( Family& family, const std::string& name ) : family_ ( family ), name_ ( name )
{
}

void Family::Child::PrintName ( )
{
	std::cout << name_ << ' ' << family_.family_name_;
}
To me, this pretty much destroys the only advantage the friend keyword has over creating a class hierarchy when coding a system. Building a class hierarchy is more preferable to creating a system for multiple reasons:
  1. Purpose and access is made clear.
    Using the friend keyword, it it not made clear that Child can access Family's private members from just looking at the Child code alone(Remember, the friend declaration is in Family and not Child). Other programmers would also have a harder time figuring out what the purpose of the class is since it doesn't seem to be coded to be general purpose. Declaring the Child inside Family, you will need to define the class as Family::Child making it clear that Child is a class that is part of a system of classes inside family and is geared towards coordinating with the other classes within that system.
  2. The names inside the hierarchy do not pollute the global scope.
    Having names like ListNode and GraphNode can be redundant especially if you are only going to use them in their respective systems that they manage. Declaring these classes in their respective systems removes their names from the global scope and as a result, you can just call them Node and remove the naming redundancy.
  3. Full encapsulation is made possible.
    You have the option of fully encapsulating a class within its system by declaring the class as a private member. Even if you #include "Child.h" outside of Family, if it is declared as private, both the compiler and intellisense won't let you have access to it making the class completely invisible outside of the Family class.
  4. Intellisense immediately informs you of a name discrepancy.
    This one is a minor nitpick but if you somehow made a mistake in declaring the name of a subclass(like declaring it as Children in Family when you define it as Child), intellisense would immediately inform you of the discrepancy when you define the class(class Family::Child would cause intellisense to freak out since it was declared as Children). Using the friend keyword, you won't find out about this until you realise that you can't access the private members of Family inside Child.
I would really like to know if anyone has other uses for the friend keyword. To me, creating systems of classes are the only reasons why I would ever use friend declaration but now that I know that making class hierarchies are much easier, I can pretty much make a system of classes solely with class hierarchies and inheritance. Whether it be a Graphics system, a Network system or a Game system, I'm going to change all my code to not use friend declaration anymore.
Zekilk

User avatar
chili
Site Admin
Posts: 3948
Joined: December 31st, 2011, 4:53 pm
Location: Japan
Contact:

Re: The friend keyword is kinda useless

Post by chili » December 10th, 2016, 1:09 pm

Hey cyborg, long time no see.

I use the friend declaration occasionally. I reserve inheritance strictly for situations where there's a clear is-a relationship.

That's quite a write-up you've made; I'll take a look at it in detail and write my ideas when I'm done. I'm always interested in hearing new ideas about system architecture.
Chili

User avatar
cyboryxmen
Posts: 190
Joined: November 14th, 2014, 2:03 am

Re: The friend keyword is kinda useless

Post by cyboryxmen » December 10th, 2016, 4:30 pm

Thanks. I pretty much came up with this programming paradigm in response to the problems I face when I first coded my own graphics class. Graphics ended up defining literally everything related to graphics from the screen to the meshes and textures themselves. I just use an index to represent the instances of those objects and pass it to the render function of Graphics to run the rendering code. The class became bloated real quickly.

I needed a way to somehow split up Graphics into multiple subclasses that will handle parts of Graphics' functionality to help organise the code. Problem is that I didn't want to make the implementations of Graphics public just so that the subclasses can access them. friend declarations has been my way of getting around that fact for a long while now but I now find that defining class hierarchies are better at this task.

Having a class hierarchy helps a lot to represent a system because you start to think of systems as a single machine made of components made of components. Those components are made specifically to manage the class they are assigned to rather than be general purpose like classes in the global scope. The hierarchy represents this by encapsulating the component in the class they are managing. If I made a Player class inside a Game class, it won't be just any Player class. It's a Game::Player class. Because of the fact that Player's sole purpose is to serve Game, it gets special privileges to Game's implementation by the hierarchy.
Zekilk

User avatar
cyboryxmen
Posts: 190
Joined: November 14th, 2014, 2:03 am

Re: The friend keyword is kinda useless

Post by cyboryxmen » December 10th, 2016, 5:00 pm

Essentially, class hierarchies help to define composition. Much in the same way inheritance defines is-a relationships, class hierarchies define has-a relationships(as opposed to uses-a relationships for aggregation).
Zekilk

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: The friend keyword is kinda useless

Post by albinopapa » December 10th, 2016, 8:56 pm

What you call class hierarchies, I call nested classes. I use the nested classes like chili does in his Snek game in the Beginner Tutorials Reloaded. The nested class is just a portion of the larger, that deals with a specific task. For instance, I have a grid of cells, I nest the Cells inside Grid and just interact with the Grid object.

As far as friend goes, I just use it when I can't find a better way of sharing data, because I hate having to write MyClass::GetData()const{ return myOtherObject.GetData(); }...I don't like having to write a Get function that calls the hidden Get function, so I friend the visible with the hidden class and write one Get function.

Most recently I have been experimenting with the MVC (Model View Controller) pattern, and I friend the Controller with the Model, so I can write the Get functions in the Controller and the controller returns the model's data. This way the Game only interacts with the Controller and the View.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

reductor
Posts: 49
Joined: October 24th, 2016, 12:23 pm

Re: The friend keyword is kinda useless

Post by reductor » December 11th, 2016, 5:29 am

friend is useful, however the use-case you are describing above nested classes are better. Even with a friend class you should avoid accessing private data if there is an accessor for it (e.g. you should add one for surname, and remove the friend all together)

Nested classes being implicit friends is only new in C++11
Using the friend keyword, it it not made clear that Child can access Family's private members from just looking at the Child code alone(Remember, the friend declaration is in Family and not Child). Other programmers would also have a harder time figuring out what the purpose of the class is since it doesn't seem to be coded to be general purpose. Declaring the Child inside Family, you will need to define the class as Family::Child making it clear that Child is a class that is part of a system of classes inside family and is geared towards coordinating with the other classes within that system.
The code within the friend (Child in this case) should be clear that it is accessing data based on what it's accessing (e.g. you can see in your code accessing a member variable which is likely private or protected).

You specify the friend within Family because within Family you need to know how it's data is changed, and as soon as you see a 'friend' declaration you know that you need to check 'Child' when you want to know what is accessing what. If this was only within 'Child' then as the designer of 'Family' you can not restrict access to data, and you can not know who has decided to be a friend.

There are many different usages for friend outside of the examples you provided.

e.g. Construct an object using private constructor (promise/future), prevent usage of the constructor from other users

Code: Select all

struct future_shared_state;

struct future
{
friend class promise;
private:
  future(std::shared_ptr<future_shared_state> shared_state) : shared_state(shared_state) {}
  std::shared_ptr<future_shared_state> shared_state;
}

struct promise
{
   future get_future() { return future{ shared_state }; }
private:
  std::shared_ptr<future_shared_state> shared_state;   
};
e.g. Defining a factory for creating an object

Code: Select all

class obj
{
friend obj make_obj();
private:
   obj();
};

obj make_obj()
{
     return obj{};
}
Most usages of friend should be rare, don't abuse it with being lazy about writing getters and setters, instead use it only when you need to it makes it harder to reason about code when you abuse it too much.

User avatar
cyboryxmen
Posts: 190
Joined: November 14th, 2014, 2:03 am

Re: The friend keyword is kinda useless

Post by cyboryxmen » December 11th, 2016, 10:40 am

I suppose nested classes would be a better way to describe them(and shorter too). To me, I still see that nested classes makes more sense semantically that they can access private members of classes that they are nested in compared to befriended classes.

Let's say that there are two classes Soccer and Bowling that have a Player class nested in them. The Player class inside Soccer won't be just any generic Player class but it is Soccer's Player class(Soccer::Player). That means that Soccer's Player is coded to use Soccer's unique properties(members) like num_goals while Bowling's Player is coded to use Bowling's unique properties like num_pins. If the class is so entwined with another class to the point where they actually require access to their private members, they should just be nested within them rather than be befriended.

I thought about all the examples you guys gave me and I still see that nesting them would be better than befriending them. Controller can just be defined as Model's Controller and Promise can be defined as Future's Promise. I do see some arguments to be made for Factory since the objects constructed aren't necessarily unique to Factory. Even so, if Factory is also the class responsible for using those objects like a Server class that also acts as a factory for Client, you can very easily say that these are Server's Clients.

Course, you should avoid using either and code your classes as generically as possible with getters and setters. In some cases where genericity makes no sense though, nested classes should be prefered to friend declarations.
Zekilk

reductor
Posts: 49
Joined: October 24th, 2016, 12:23 pm

Re: The friend keyword is kinda useless

Post by reductor » December 11th, 2016, 11:23 am

I suppose nested classes would be a better way to describe them(and shorter too). To me, I still see that nested classes makes more sense semantically that they can access private members of classes that they are nested in compared to befriended classes.
Nested classes is the official name, while they are implicitly made friends they do not always access private or protected data, and nor should they.
Let's say that there are two classes Soccer and Bowling that have a Player class nested in them. The Player class inside Soccer won't be just any generic Player class but it is Soccer's Player class(Soccer::Player). That means that Soccer's Player is coded to use Soccer's unique properties(members) like num_goals while Bowling's Player is coded to use Bowling's unique properties like num_pins. If the class is so entwined with another class to the point where they actually require access to their private members, they should just be nested within them rather than be befriended.
A Soccer player should not require access to Soccer's private data, doing this is going to make it harder to reason about, this approach to design will not scale properly. You also create a large amount of dependencies (not separation of concerns) that can make testing much harder.
I thought about all the examples you guys gave me and I still see that nesting them would be better than befriending them. Controller can just be defined as Model's Controller and Promise can be defined as Future's Promise.
No nesting would send the wrong message to the reader, adding these as nested classes creates more dependency on the class they are inside, when they are not dependent on that (e.g. Controller dependent on Model, Future dependent on Promise) so you can not use them outside of this context. There are many other issues with this (e.g. implementing them in the same file, interfaces are inside each other, etc)
I do see some arguments to be made for Factory since the objects constructed aren't necessarily unique to Factory. Even so, if Factory is also the class responsible for using those objects like a Server class that also acts as a factory for Client, you can very easily say that these are Server's Clients.
That server client assumes that one always owns the other, and nothing else owns it, in which case it might be reasonable to do a nested class, but I would avoid it because of the reasons mentioned above.

I can provide many other useful examples.

e.g. Defining a stream operator

Code: Select all

class obj
{
int x;
friend std::ostream & operator<<(std::ostream & os, const obj & o )
{
 return os << o.x;
}
};
Course, you should avoid using either and code your classes as generically as possible with getters and setters. In some cases where genericity makes no sense though, nested classes should be prefered to friend declarations.
This isn't about generic code but a separation of concerns and encapsulation.

Other issues with nested classes:
  • Privileged access (This is what your wanting it for, but it is an issue because the designer of that nested class can now do what they want with your objects internals, to refactor one you may need to refactor the other)
  • All of the enclosing classes symbols are now part of the enclosed classes symbol lookup, this can cause confusion
  • No way to forward declare nested class (Need the enclosed class)
  • Requires enclosing class to include nested class inside it's definition (Distracting from the interface of both)
  • Header dependency (Similar to no way to forward declare)
  • Probably more that I can't think of off the top of my head

User avatar
cyboryxmen
Posts: 190
Joined: November 14th, 2014, 2:03 am

Re: The friend keyword is kinda useless

Post by cyboryxmen » December 11th, 2016, 12:09 pm

I suppose I phrased that last paragraph wrongly by emphasising on genericity and not the other various reasons to keep the class separate. Basically, I am aware of the downsides that friend declarations and nested classes have with privileged access, encapsulation, separation of concerns and every other headache associated with them. I know that you normally want to avoid using these features as much as possible and I still do whenever I can. I was mainly emphasising on the advantage of using nested classes over friend declarations in situations where you need to provide privileged access. That combined with my half assed examples didn't make my intentions clear.

Regardless of that, I suppose that concerns with forward declarations give friend declarations a real advantage over nesting your classes. This is something I'll consider when choosing to nest a class in another class or just declare it as a friend.
reductor wrote: No nesting would send the wrong message to the reader, adding these as nested classes creates more dependency on the class they are inside, when they are not dependent on that (e.g. Controller dependent on Model, Future dependent on Promise) so you can not use them outside of this context. There are many other issues with this (e.g. implementing them in the same file, interfaces are inside each other, etc)
I'm not sure what you meant by that. Aren't Future and Promise, Model and Controller already closely assosiated with each other that they are used in context with each other? More importantly, how does this make using the friend declaration more advantageous in comparison?

Code: Select all

friend std::ostream & operator<<(std::ostream & os, const obj & o )
{
 return os << o.x;
}
I was unaware that you can a friend declaration and function definition at the same time. Though to be fair, I rarely use something like this since I prefer using getters and print the output myself. It's still a nice syntax to use though
Zekilk

Post Reply