The course is part of this learning path
In this course, we will build on your existing foundational and object-oriented oriented skills and enhance them by looking at templates, the Standard Template Library, and other skills to help you in your builds.
Learning Objectives
- Learn about function templates and class templates
- Learn how to write efficient and excellent code with the data structures and algorithms in the standard template library
- Learn about smart pointers to manage dynamic memory automatically
- Understand friend functions, friend classes, and operator overloading
Intended Audience
- Beginner coders, new to C++
- Developers looking to upskill by adding C++ to their CV
- College students and anyone studying C++
Prerequisites
To get the most out of this course, you should have a basic understanding of the fundamentals of C++.
In the last lecture we discussed smart pointers which are very powerful when dealing with dynamic memory and clean up after themselves. In this lecture we'll discuss a rather controversial topic that many developers avoid. However, you may come across these, so it's important to know about them. I'm talking about friend functions and friend classes. Many developers argue that these violate encapsulation because their purpose is to give non member functions and classes outside of a class hierarchy direct access to nonpublic, that is protected and private, members of a class. In general, it is best to avoid friend functions and classes, but they can be useful in some rare situations. Conceptually and syntactically they are very simple. Let's create a project called FriendFun.
All right, here's FriendFun. We will create a new project, 'Empty Project', 'Next' and we'll type FriendFun, capitalize fun there. All right so, we're going to copy over the Rectangle class that we've worked with many times before into our project. So, I happen to have my repo open over here and I'm going to grab DynamicRectangles or go in here and grab the Rectangle class. I'm going to copy these and I'm going to go back to the repos and then for FriendFun, I'm going to put it in the project folder under FriendFun. So, that's our first step, but then we also have to add them. So, I'm going to go into the project, 'Header File', 'Add', 'Existing item' and then I will pick the dynamic, actually it's under FriendFun, sorry. It's already under FriendFun, so I'm going to grab both of these and drag Rectangle.cpp down here. I'm going to right click 'Source Files' now and add a new item for our main file. Main.cpp. There we go, good. Now we need to do a little bit of modification but first all type in our little skeleton here.
All right, okay Rectangle.h. For Rectangle.h, I'm going to add a friend. Now friends aren't really public or private, they're just declared as friends. So, friend void changeData and then it will take a Rectangle reference that will just call rect. But we don't define this inside of Rectangle.cpp because it is not a member function, it is not a method. It will actually be defined inside of main. So, I'm going to grab this up to void and copy that because you don't use the friend keyword in main. But in main I don't need the prototype because the quote prototype is inside the class. So, we'll put that below main right there. And all I'm going to do is prove that I have access to the private data inside of rectangle. So, this width equals 100, I'm directly accessing it and you might think, well wait a minute we have a set length, why do we need this? Well, it's for the purpose of demonstrating that we can access the private data. So, we have 100 x 100 and then what I'm going to do is I'm going to do a little bit of a test inside main. We'll just create a rectangle object, a 10 x 25 and then we'll print out the area and it should be 250. Good. Now I'm going to call changeData and it will take r1 by reference and now I'll say, "now r1 area:" is r1.area and of course let's run it. Debug, start without debugging. All right, there we go. So, we've proven that we do have in fact, even though changeData is not a member function, we defined it inside of main. It's just a regular old function. Even though we've defined it outside and it isn't a member function, it has access to the members when you pass in a reference to the rectangle. So, any rectangle we can access the private data. A lot of people of course have problems with that. So, there's 250 was the original area because of 25 x 10 and then 100 x 100 gives us 10,000. Good, good. So, it works great. To recap what's happening since changeData isn't a member function, again we have to grant friendship to it from inside the class.
Now this happens in the header, right? We don't really modify the CPP file because that's implementation but if we're doing it from a header and we have separate compilations we just do it inside the class declaration. The changeData function is defined outside the class as a non-member function. We pass a rectangle object reference to it and change the private data directly. Since changed data is a friend of the Rectangle class, it has access to nonpublic data members. It would also have access to any nonpublic methods as well, but we don't have any nonpublic methods. So, you can probably see why this again could cause some nervousness with developers and why a lot of developers don't like it. It is literally violating encapsulation. This means that we are able to break through, so to speak the barrier that encapsulation provides us; code outside of our classes accessing, in our case, private data. And in addition to friend functions though, we can also declare an entire other class as being a friend and all of that classes, methods can access the private and protected data of the class granting the friendship.
So, let me do a demonstration with a class that will be very simple called RectangleHelper class. We'll just create a RectangleHelper.h file just to keep things very, very simple. So, Header, 'Add', 'New item' and this one is going to be called RectangleHelper. So, RectangleHelper.h. And of course all right. So, #ifndef RECTANGLE_HELPER_H, #define RECTANGLE_HELPER_H. So, in here we'll just put one member function and that's the only thing it will be, modifyRectangle. We also need Rectangle.h to be included here. All right so, modifyRectangle(Rectangle& rect), rect.length. I'm going to type it. It's not going to like it, but length equals 500 and we are going to get an error. I'm going to actually hit 'undo', Control Z on Windows. I want to set it to 500. You'll notice if I hover over this it says, member "Rectangle::length" is inaccessible. And then I do the same thing for width, member "Rectangle::width " is inaccessible. That means hey, it's private you can't modify that. Well we're going to prove that that's not the case because if I go into Rectangle.h, I can actually declare; RectangleHelper. And this is actually called, it's granting friendship but it's also doing what's called a forward declaration because it has to say, hey, I know about this thing called RectangleHelper and it's a class so you tell what it is.
So, just like you would with the function. So, this is a very common way to do this if you have this class in a separate file. Now some people might say, why don't you just include RectangleHelper and then you can just say friend RectangleHelper. Well then we have a problem. We'd have Rectangle including RectangleHelper and RectangleHelper including Rectangle which then includes RectangleHelper that includes Rectangle. So, it ends up being this problem and the compiler does not like that. So, forward declarations are saying, hey, there's this thing that's going to exist called RectangleHelper and you just need to be aware of it. So, that's all that does. Now you'll notice though. Now if I go back to helper, do you see how the errors went away? So, inside the RectangleHelper class itself again, we access the rectangle object reference and like the changeData friend function are able to access the private data directly.
Now let's modify main. So, I need to include RectangleHelper, RectangleHelper. All right, there we go. And we're going to inside of main. I'll create a RectangleHelper named helper, okay. And then after the area is created here and we already did a changeData test using a friend function. Now we're going to do it using the friend class. So, helper.modifyRectangle. We're going to pass r1 to it and the whole idea is to say, hey this class and any of its methods have access to modify directing on, what did it do? It sets them each to 500. So, we should end up with a different area, right? So, we'll say, "after class friend, r1 area:" r1.area and there we go. And of course let's run the program. Debug, 'start without debugging'. All right, after class friend r1 area 250,000. All right so, proven that this class now has access to the private data inside the other class. So, RectangleHelper has the access to Rectangle. It's important to note that friendship is granted, not taken.
So, you can't go into say RectangleHelper and say, hey, I'm a friend of this class, so I get to access their private data or private members or protected members. You can't just do that. The class that's being access has to explicitly say, hey, if you have this class signature or function signature and return type you can access this. So, it has to grant friendship. So, before moving on I have a challenge for you. I'd like you to create an additional friend function, a void function named printData. You will grant friendship from within the Rectangle class and then define printData to take a Rectangle reference as its parameter and to directly access the private length and width and print it to the console. Make sure to test this within main as well. So, pause the video come back when you're done or if you need some help. How did that go for you? Let's work on it together. So, inside of Rectangle.h, I'm going to grant friendship.
So, friend void printData is what we called it and it will take a rectangle, rectangle right there and then we will go into main and we will define it similarly to what we did with the changeData near the bottom. So, I'll say void printData. There we go and we're going to print out "length: " rect.length. So, we print it directly and then we're going to say "width: " rect.width again printing directly, not calling the getter. Good. So, we need to call this from main and let's see what we got here after the class friend right there. I'll say "printing data using friend:" right there. We'll say printData(r1) and of course we need to run this. Debug, 'start without debugging' and there you go. We've got the length and the width, each set to 500. We've proven that we can access the private data using our printData function. All right, seems to work just great. Good job everyone. In the next lecture, we'll discuss operator overloading. I'll see you there.
John has a Ph.D. in Computer Science and is a professional software engineer and consultant, as well as a computer science university professor and department chair.