We've seen how we can write generic functions using c++ templates.
In this video, we'll take it a step further,
and we'll learn how we can use c++ templates to create generic classes.
So what is the c++ class template.
A class template is similar to a function template
but instead of creating a generic function,
we'll be creating a generic class.
So this implies generic attributes, constructors, destructors,
methods and so forth.
Just like we can plug in any type to a function template,
we can do the same with a class template.
We've already used class templates in several places in this course,
the most obvious is when we created vector objects.
We didn't just say I want a vector, we said I want a vector of a specific type,
and that's where the class templates come in.
So the class template is a generic blueprint
that the compiler uses to create specialized classes.
That's what metaprogramming is all about.
As I mentioned in the previous slides, templates can be very complex.
Class templates, in particular, can be very complex.
Imagine combining inheritance, dynamic polymorphism,
operator overloading, templates and other advanced c++ features together,
super powerful but also complex.
So let's see how we can use class templates.
Let's say we want a class that will hold an integer and a string,
and we want that class to be called item.
We can write a simple class as follows.
Note that we have 2 private attributes, name and value.
A constructor that initializes those attributes to those provided by the user,
and we have a get name and get value getter methods.
Of course, we could add other methods, but we won't in this example
since the purpose is to focus on how we can make this class generic.
So let's suppose that now we need a class that's similar to the item class.
Except instead of an int and a string, we want to model a double and a string.
What can we do?
Sure, we can create a new class, just like we did for item that has a double and a string.
But we'd have to name it something other than items since we can't overload
class names in the same namespace.
And we don't want to create a complex hierarchy with dynamic polymorphism.
So let's see how we can use class templates
to allow us to just write the blueprint for the class.
First, we can replace the type of the value attribute from int to t in this case.
But remember, this can be any name.
Then we go through the class. And wherever we used value
or return values type, we replace it with a t.
That's pretty much it. Now, of course, this won't compile.
We need to tell the compiler that this is a template class.
And we can do this by adding the template declaration
before the class declaration and provide the template parameter.
This is a pretty simple example,
but it sure makes the item class much more generic and much more useful.
Now if we want to create instances of items,
we can simply provide the type of the value in the template parameter and the compiler
will take care of generating the correct class from the template.
In the first example, we're creating item1 as an item with a value
of type int and a name of type string.
Name will always be of type string, so we don't have to provide it as the template parameter.
In the second example, value will be of type double.
In the third example, value will be a std string.
And in the final example, we're creating a vector that holds items,
and those items will have value as an integer.
That's it.
Just as with function templates.
There's no limit to the number of template parameters you can have.
And of course, they may be of different types.
In this example, we're creating a class template called my pair
that models a pair of data items.
The types of these data items can be different if we wish or they can be the same.
Note that in this case, we use a struct
since all we want to do is associate first with second.
We're just modeling data so we won't bother with constructors, methods and so forth.
Now if we wanted to create my pair objects, it's really easy.
In the first example, we create p1 as a my pair of std string and integer,
and we initialized it to Frank and 100.
In the second example, we create p2 as a my pair of integer
and double.
And we initialize it to 124 and 13.6.
And in the last example, we create a vector that holds my pair objects,
and those objects are int double pairs.
This my pair class can be a very handy class
since it can associate any 2 pieces of data of any type.
In fact, it's so useful that it's already defined for us in std pair.
We need to include the utility header file,
and then we can use the std pair class, which is implemented
very much like the way we implemented my pair.
In addition to the pair template class, c++ also has a template class named tuple.
It allows you to have a fixed number of elements of any type.
It's not just limited to 2 items, like the pair class.
Okay. So let's head over to the IDE, and we'll see these class templates,
and we'll also implement our own template-based array class.
Okay. So I'm in the IDE. I'm in the section 20 workspace,
and I'm in the ClassTemplates project.
In this project what I'd like to do is go over those 2 templates that we talked about in the slides,
and then we'll switch to this other project up here class template array,
and we'll implement an array class based on a template.
Okay. So first things first. I put a little note here that's important.
Template classes are typically completely contained in header files.
So we would have the template class in item.h,
and I'm talking about this template class that I'm going to talk about right here.
And there would be no item cpp file associated with it.
Now that makes sense because what we have is a blueprint.
The compiler is not compiling this blueprint,
it's basically using it to create
a specialized version of it that will be compiled. So it's important
if you start putting stuff in cpp files, you're going to get -- run into all kinds of issues.
There are ways to get it to work,
but they're beyond the scope of this course. So for this course, put all your template classes
in their own .h file.
Let's talk about this template class.
This is the one we did in the slides, and we'll do this one first,
and then we'll do this smaller one down here second.
So what have I got here. I've got a template class.
I know it's a class right here. It's not a function, it's called item.
And here's my template parameter right here, T.
So what do I know about this? Well, I know I've got a class called item,
and it's got 2 private attributes. One is a string called name
and one is a value of type t, that's what's going to be replaced or plugged in.
I've got a constructor here that just constructs it based on my name and my value
that are being passed in.
I've got to get name, and I've got to get value.
And notice, the get value returns a t.
So that's pretty straightforward I think.
And now let's look at the second template down here. This is the pair template that we created.
And we're going to use a struct here instead of a class, but -- again, it's perfectly fine.
We're only modeling data in this case.
So we've got this template, parameter list right here
where we've got a t1 and a t2 and they could be the same or they could be different.
So all we're doing is we're saying the first item here. In other words, this is this is just a name,
an attribute name. First is the t1, second is a t2.
So what we're really doing is associating 2 data types
and calling it my pair. Okay. Just like we did in the slides.
So let me go down to the main, and I'll show you some examples of of this,
and I'll walk through these one at a time.
So first thing. On line 30 right here,
I'm creating an item1 object
and it's of type item.
That's my class. Value is going to be an integer.
So remember, I have a string and I have a value.
In this case, I always have a string first.
And then this guy right here,
that's going to be the t.
That can be replaced with any type I want. So in this case,
item1 will look something like this. Item1 is an object.
It has a name, attribute,
and it's got a value attribute. The name is always going to be a string.
In this case, it's going to be Frank and the value,
that's our template parameter. That can be any type I pass into this, right.
That's what I'm doing right here. So in this case, it's going to be an integer,
and it's going to be 100. So that's kind of what's going on.
So when I display item1 get name,
I'm getting this guy right here, and that's going to display Frank.
And then when I say item1 get value, it's going to get the 100
here. Okay. So that's that example.
Let's take a look at this one here on line 33,
and we'll run these in just a bit. So here I'm creating, again,
an item2, that's my object name.
And it's an item, but look at my template parameter here.
I'm saying the second one is a string. So what that would look like is
now I've got item2.
It has a name. It also has a value.
In this case, they're both going to be strings. The first one will be Frank,
that's my name,
and value will be professor.
Now in the output statement, item2 get name. We'll get Frank
right here,
and item 2 get value. We'll get professor, and that's what will be displayed Frank professor.
Now this one here is a little more complicated. And I did this one on
purpose so just so you can see how you can build these things up
because sometimes you'll see these defined this way.
Now let's walk through this one really slowly.
Item3 is my object. So let me write that down. Right here, this is item3.
And we know that item3 is an item.
Okay. So it's going to have a name,
and it's going to have a value.
But what are we saying that the value is? Well, in this case, the value is another item,
but that other item has a string as the value.
Makes sense? So this value here -- well, let's see name is Frank.
Let's get rid of that one first. Let's do that one really easily here.
And I'm using my initializer right here to do it.
And now my value is going to be an item.
And that item has a name
and a value.
Okay. So you can see that I'm just building them up, I'm nesting one within the other.
So in this case, the name will be c++
and the value will be professor in this case.
All right. So now when I say item3 get name,
I'm getting this guy right here, that's item3's name.
In order to get to these guys and here I've got to go through the value.
So in this case, I'm saying item3 get the value.
Now I've got this item, and then I'm saying get the name.
So now I've got c++.
And over here, I'm just saying item3 get value, that takes me to the value,
that is an item. Remember, that I'm going to get the value from the value,
which is going to be professor in this case.
So when I print this out, I should get Frank 100,
Frank professor, Frank c++ professor.
All right. So let me clear the screen, and we'll give it a run.
And what we've got here is we've got Frank 100, Frank professor,Frank c++ professor,
just what we expected.
Okay. So let me make a little bit more room here.
And now what I'm going to do is I'm simply going to take some items
and create a vector of those items.
So in this case, I'm creating a vector,
and that vector is going to hold
item objects.
And in this case, the values will all be doubles.
Remember, the names are always strings. But in this case,
the values will be doubles.
And I'm just going to push back 3 of them.
So I'm going to say Larry 100.0, Moe 200.0
and Curly 300.0.I'm creating items here,
and I'm pushing those items on the vector.
And then all I'm doing is looping through the vector with a range-based for loop right here.
So for each item in that vector, get its name,
get its value.
That's it. Now that's pretty cool, because I'm doing all of this using a template class.
So if we run this, we should see Larry 100, Moe 200 and Curly 300.
So let's give that a run.
And there you go.
Larry is 100, Moe is 200 and Curly is 300,
just what we expected.
Okay. So now let's look at the pair.
That's this last piece right here. So I'll uncomment that out,
and we will run this now. You can see what's happening here. I'm creating my pair.
And my pair is going to associate
a pair of types, right, in this case a string and an integer.
There's the string, there's the integer.
Here I'm associating an integer and a double.
There's the integer, there's the double.
This guy is called p1, and this guy's called p2.
And if I want to display those values, all I have to say is
p1.first. To display the second, I would just say p1.second,
and that will display Frank 100.
Then for p2, we could do the same, p2 first, p2 second, we'll display
124 and the 13.6. So let's give this one a run.
And you could see down here,
we've got Frank 100 124 13.6.
So this gives you an idea of using 2 pretty simple
template classes that we've created.
Now I'm going to pause this for a second, and I'll be right back, and I'm going to jump right into this class
template array project.
And what we'll do there is we'll create another class template for an array.