Join Bill Weinman for an in-depth discussion in this video Function members, part of C++ Essential Training.
- [Instructor] C++ classes can have member functions that act as methods for the class. Here I have a working copy of class.cpp from chapter six of the exercise files, and this is a very simple class. It has a data member, which is an integer, i, and a couple of function members for setting and getting the value. The data member is private by default. This is by design because it's considered best practice to encapsulate object data and to use object functions to access that object data.
So we have those functions for setting and getting the value. Now we declare an object of type C1 called o1, and we use setvalue to set the value of the data member and getvalue to get the value of the data member, so when I build and run, you notice we get the expected result where the value is 47, which is the value that we set here from i. Now, it's also considered best practice to separate the interface from the implementation.
So the class definition here is considered the interface. And when they're just one line like this, I'll often include the code for a function member in the interface, just because it's convenient. But it's usually considered best practice to put them separately. And so we'll separate these out like this. We'll make these just the definitions in the interface and these will have the implementation separately here.
We can associate these functions with the class by putting them in the class's namespace by using the name of the class with the two colons after that. And I'll simply make these into normal-looking functions. And there we have it. Now, when I build and run, you'll notice that this works exactly as we expect, and yet now we've separated the implementation from the interface. So the interface is the class definition, and the implementation is these member functions.
Now, most of the time in production code, you'll see the member functions actually in a separate file. The class definition, or the interface, will be in a .h file, or sometimes .hpp, and the implementation, the member functions, will be in a .cpp file separate, but usually with the same name as the class. For our purposes, we'll usually just put it all in one file. This is just for convenience, just to make it easy to work with in our exercises. For actual working code, I would usually find it even more convenient to separate out the interface and the implementation into separate files.
Now, while we're talking about member functions, there's one more important piece that you need to understand. And so I'm going to come down here, and I'm going to make a copy of this. And I'm actually going to create a separate object called o2 and we'll use o2.getvalue. And when I build and run here, you'll see that o2.getvalue is zero because the default value for a C1 object or a class is zero, we have it defined up there.
Now, if I change this object to const, you'll notice that my getvalue no longer works. I get a strange message that's hard to understand, but basically what it's saying is that the getvalue function no longer has access to the object because getvalue is not defined as const safe. We define it as const safe by putting the const keyword out there and we need to do it also in the definition.
And this makes that function const safe, and it says, okay, now you have access to data in a const object. Without that, it will not give access to the data. So by doing this, of course it builds and runs, but also gives us access to this data. Of course, we're no longer able to change that data. If I were to say plus plus i here, I can't do that with a const member function. I need a separate non-const member function, and here's where this gets cool.
I can take out the const in this one, and I can take out a plus-plus in that one. I need to do the same thing in the definition, like that. And I'm go ahead and put in a little puts here, so that we know which of these is which. So this one is const getvalue, and this one, I'll call it mutable, because that's what we call a function that is not const, we call it mutable.
And now when I build and run this, you'll notice that I'm calling both of these. For our mutable object, our object that does not have const, we're calling the one that says mutable and its value is 48, because it's incrementing. For the one that's const safe, we're calling the const version and its value is zero because it does not get incremented. So we have here two functions with the same name, the only difference is that one is const safe and one is not.
And so the const safe version only gets called for const objects. If I take out that const there and build and run, you notice that now it's calling the mutable version. So the rule of thumb is that a const safe function may always be called, and a non-const function may only be called by non-const objects. So if I have a function that doesn't actually need to change anything in the object, I'm always going to use the const keyword to make it const-safe.
And if I have a function that actually needs to change something, then I don't use that. And if I need to have two versions for objects that are const and objects that are not, I can use the same name and overload that function and have a version for mutable objects and have a version for const safe objects. So member functions are what make object-oriented programming possible. As we go through the rest of this chapter, you'll see that most of the other features of C++ objects are implemented with member functions.
- Setting up Xcode and Visual Studio
- Statements and expressions
- Primitive arrays and strings
- Data types
- Classes and objects
- Standard Library and Standard Template Library