Powered by
/src$ make
  • Home
  • About
  • Directory
  • Contact
  • Home
  • About
  • Directory
  • Contact

C++ Class, Template, and Custom Data Structure Guide

1/13/2018

 
To see the video where I walk through all of this code with you, please click here for the youtube video.

Introduction: Manageable Goals

The goal of this blog post is to gain an understanding (with example code) of how C++ Classes and Templates work, as well as being able to build our own custom data structure. 

Now, I don't want to go into classes and objects, because that conversation confuses most beginners. For now, you don't need to know about those. We just want to be able to create and use a class, then learn about templates, and then use that knowledge to build our own custom data structure. 

The following code is also available on github, found here.

A Brief Warm-Up: Hello World and Functions

Let's start with a brief warmup. 
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#include <iostream>
using namespace std;

void SayHi(string name)
        {
        cout << "Hello, from " << name << ".\n";
        cout << "Also, Hello World!\n";
        }

int main()
        {
        SayHi("srcmake");
        return 0;
        }

Here we have a basic Hello World program, where the code for our output is in the function SayHi. Functions are useful in that we can separate some code away from main into it's own special little bundle that we can call whenever we need it. Often, it saves time as well if we use the function multiple times. 

Not too hard, right? Let's move on to the next step. 

Classes

Classes are similar to functions in that we create a blueprint for what we'd like to have the class do, and define functions that we can use whenever we'd like. 

The following code demonstrates the ability to create a class, that holds some functions and variables, and how we can use that class. Read the comments to see how everything works.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
// Copyright srcmake.com 2018.
#include <iostream>
using namespace std;

//////////////////////////////////////////////////////
// A class for math operations. 
class MyMathClass
        {
        // Let's prototype the variables and functions that this class will have.
        
        private:
                int favoritenumber;
        public:
                MyMathClass();
                MyMathClass(int);
                void SetFavoriteNumber(int);
                int Add(int, int);
                
                // Let's just fill this function inside of the class itself, for fun.
                void PrintFavoriteNumber()
                        {
                        cout << "The favorite number is " << favoritenumber << endl;
                        }
        };

// Use a constructor to set our favorite number up as 7. 
MyMathClass::MyMathClass()
        {
        SetFavoriteNumber(7);
        }
        
// Overload the constructor so that we can set it ourselves when we create the class, if we'd like to do so.
MyMathClass::MyMathClass(int fave)
        {
        SetFavoriteNumber(fave);
        }

// Our SetFavoriteNumber method can access the private variable "favoritenumber" since it's in the same class.
void MyMathClass::SetFavoriteNumber(int fave)
        {
        favoritenumber = fave;
        }

// Utility function to add two numbers.
int MyMathClass::Add(int a, int b)
        {
        return a + b;
        }
//////////////////////////////////////////////////////



//////////////////////////////////////////////////////
// Our program always starts in the main function.
int main()
        {
        // Use our Class by making an object of it.
        MyMathClass class1;
        
        // Call the class's [public] methods by invoking them from the class object that we created.
        // The default favorite number should be set to 7.
        class1.PrintFavoriteNumber();
        
        // This time, let's override the default constructor.
        MyMathClass class2(20);
        class2.PrintFavoriteNumber();
        
        int sum = class2.Add(5, 5);
        cout << "The sum of 5 and 5 is: " << sum << ".\n";

        /* Not to useful yet, but that's how you use a class. */

        return 0;
        }
//////////////////////////////////////////////////////
Output:
​The favorite number is 7
The favorite number is 20
The sum of 5 and 5 is: 10.

Same Class Code, But Separate Files

Okay so we can make and use classes in C++, but to be honest, it's a bit unreasonable (and not that helpful) if you just keep everything in one file. That's not how projects actually work. (And it leads to a lot of clutter and often poor documentation.)

We're going to use the exact same class as above, but we're going to separate the class code into a new file. 
Our class code is the exact same as before, copied into a new file named "MyMathLibrary.cpp". The only addition to this file is the "#include<iostream>" and "using namespace std;", since the class's code still needs those.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// Copyright srcmake.com 2018.
// This entire class definition is copied and pasted from before. 
#include <iostream>
using namespace std;

//////////////////////////////////////////////////////
// A class for math operations. 
class MyMathClass
        {
        // Let's prototype the variables and functions that this class will have.
        
        private:
                int favoritenumber;
        public:
                MyMathClass();
                MyMathClass(int);
                void SetFavoriteNumber(int);
                int Add(int, int);
                
                // Let's just fill this function inside of the class itself, for fun.
                void PrintFavoriteNumber()
                        {
                        cout << "The favorite number is " << favoritenumber << endl;
                        }
        };

// Use a constructor to set our favorite number up as 7. 
MyMathClass::MyMathClass()
        {
        SetFavoriteNumber(7);
        }
        
// Overload the constructor so that we can set it ourselves when we create the class, if we'd like to do so.
MyMathClass::MyMathClass(int fave)
        {
        SetFavoriteNumber(fave);
        }

// Our SetFavoriteNumber method can access the private variable "favoritenumber" since it's in the same class.
void MyMathClass::SetFavoriteNumber(int fave)
        {
        favoritenumber = fave;
        }

// Utility function to add two numbers.
int MyMathClass::Add(int a, int b)
        {
        return a + b;
        }
//////////////////////////////////////////////////////

Next, we need a header file that prototypes the class and functions for when we include them in our main file. 
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Copyright srcmake.com 2018.
// We have to include the using namespace std in this file only because we have endl in this file.
using namespace std;

// We must include the entire class "blueprint" in this header file. 
class MyMathClass
        {
        private:
                int favoritenumber;
        public:
                MyMathClass();
                MyMathClass(int);
                void SetFavoriteNumber(int);
                int Add(int, int);
                
                /* Since we wrote the function definition inside of the class 
           (as opposed to prototyping it, like the others), we must 
           keep it defined in the header file. This is poor programming.
           Always keep your class definition as minimal as possible, and 
           define the functions outside of it. */
                void PrintFavoriteNumber()
                        {
                        cout << "The favorite number is " << favoritenumber << endl;
                        }
        };

This is pretty much the same code as our class blueprint, but notice how we need to include the "using namespace std;" and the function definition because of our poor choices. (Always define your class functions outside of the class. The class itself should be as clean as possible.)
In a file named main.cpp, we have the same "main" function that we did before. The only difference is that we make a reference to the header file with the #include "MyMathLibrary.h" line. This lets us use that file's code in this one.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Copyright srcmake.com 2018.
#include <iostream>
#include "MyMathLibrary.h"
using namespace std;

//////////////////////////////////////////////////////
// Our program always starts in the main function.
int main()
        {
        // Use our Class by making an object of it.
        MyMathClass class1;
        
        // Call the class's [public] methods by invoking them from the class object that we created.
        // The default favorite number should be set to 7.
        class1.PrintFavoriteNumber();
        
        // This time, let's override the default constructor.
        MyMathClass class2(20);
        class2.PrintFavoriteNumber();
        
        int sum = class2.Add(5, 5);
        cout << "The sum of 5 and 5 is: " << sum << ".\n";

        /* Not to useful yet, but that's how you use a class. */

        return 0;
        }
//////////////////////////////////////////////////////

​Don't forget to add MyMathLibrary.cpp to be compiled if you're using a terminal. The commands for this set of code are: 
g++ main.cpp MyMathLibrary.cpp -o run
./run
The output is the same as before.

Templates

To advance a bit further, we're going to learn about templates in C++. On a basic/student level, you won't really need to know what templates are. But if you want to actually build a real class, then templates are necessary and is the difference between good coding and poor coding.

A template is basically when you want to use the same code for your functions, but have it work regardless of data type. Let me show you an example. 
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <iostream>
using namespace std;

int Add(int a, int b)
        {
        cout << "Integer addition of " << a << " and " << b << ": "; 
        return a + b;
        }
        
double Add(double a, double b)
        {
        cout << "Double addition of " << a << " and " << b << ": ";
        return a + b;
        }
        
long Add(long a, long b)
        {
        cout << "Long addition of " << a << " and " << b << ": ";
        return a + b;
        }

int main()
        {
        cout << Add(5, 5) << endl;
        cout << Add(5.5, 5.5) << endl;
        cout << Add((long)5, (long)5) << endl;
        
        return 0;
        }
Output:
Integer addition of 5 and 5: 10
Double addition of 5.5 and 5.5: 11
Long addition of 5 and 5: 10
Notice how we have an "Add" function, there are three different data types that it could return...
Let's use a template this time.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
using namespace std;

// Declare a template that uses variable type T. (T could be int, double, etc.)
template<typename T>
// The same add function, but use variable type T.
T Add(T a, T b)
        {
        cout << "T addition of " << a << " and " << b << ": "; 
        return a + b;
        }

int main()
        {
        cout << Add(5, 5) << endl;
        cout << Add(5.5, 5.5) << endl;
        cout << Add((long)5, (long)5) << endl;
        
        return 0;
        }
The output is the same as before.

You can see that inside of needing three separate functions, we just needed one. 

Okay so this seems cool and all but...how does this relate to classes? And are templates even that much better? Well...

Our Own Custom Data Structure

Okay so real talk, if you want to be a godly programmer and have everyone use your code...if you want to make a library...if you want to work on real projects...then you need to use classes and templates. 

Have you ever used vectors before? Or stacks? Queues? I have another article that goes over how these data structures work, but basically, some pro people made libraries for these data structures, they implemented all the necessary tools (pushing, popping, sorting, etc.) and packaged it up neatly for anyone else to be able to use. 

Are classes necessary? Yes, the classes are necessary.
​Are templates necessary? Yes, look at the following code.
1
vector<int> myvec;
The class name is "vector", and since we want a vector of "int"s, the vector class initializes everything for integers. As a user of the vector class, we have a very easy time since the class is so well made.
Since our inspiration is being able to make a library that other people can use, let's make a quick coding example that puts together everything we've learned so far by creating our own data structure. (We'll keep it VERY simple.) 

We're going to create our own stack library. Let's start with our stack implementation. We put our code in a file named srcstack.t.hpp (because template code can't be in a separate cpp file):
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
// Copyright srcmake.com 2018.
// "Templated code shouldn't be in a .cpp file. Put them in an hpp file."
#include <iostream>
#include <vector>
using namespace std;

//////////////////////////////////////////////////////
// A class for our stack.
template<class T> 
class srcstack
        {
        private:
                vector<T> stack; // This vector holds our stack items.
        public:
                int size();         // The size of our stack.
                void push(T);       // Add an item to the end of T. 
                void pop();         // Remove the item.
                T top();                // Get the item that's on top of the stack.
                bool isEmpty();     // Check if the stack is empty.
        };

// Return the size of our stack.
template<class T>
int srcstack<T>::size()
        {
        return stack.size();
        }

// Add an item to the stack.
template<class T>
void srcstack<T>::push(T item)
        {
        stack.push_back(item);
        }

// Remove the top item from the stack.
template<class T>
void srcstack<T>::pop()
        {
        if(isEmpty() == false)
                {
                stack.pop_back();
                }
        }
        
// Get the top item from the stack...
template<class T>
T srcstack<T>::top()
        {
        if(isEmpty() == false)
                { return stack[stack.size()-1]; }
        }

// Is the stack empty?
template<class T>
bool srcstack<T>::isEmpty()
        {
        // If the stack size is 0 then return true. Otherwise we have items so return false.
        return size() == 0 ? true : false;
        }


//////////////////////////////////////////////////////

As you can see, we implement a very simple stack class. We have the necessary stack functions, and we include the "template<class T>" line above each of them since they're part of a class that's a generic type.

Our main function simply includes our template .hpp file and demonstrates the use of our new class:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// Copyright srcmake.com 2018.
#include <iostream>
#include "srcstack.t.hpp"
using namespace std;

//////////////////////////////////////////////////////
// Our program always starts in the main function.
int main()
        {
        // Create a bool stack.
        srcstack<bool> bstack;
        bstack.push(false);
        bstack.push(false);
        bstack.push(true);
        
        // We expect to see true -> false -> false
        while(bstack.isEmpty() == false)
                {
                cout << bstack.top() << " ";
                bstack.pop();
                }
        cout << endl;
        
        // Now an integer stack.
        srcstack<int> istack;
        istack.push(5);
        istack.push(4);
        istack.push(20);
        istack.pop();
        istack.push(3);
        istack.push(2);
        istack.push(1);
        
        // We expect to see 1 2 3 4 5 
        int n = istack.size();
        for(int i = 0; i < n; i++)
                {
                cout << istack.top() << " ";
                istack.pop();
                }
        cout << endl;
        
        return 0;
        }
//////////////////////////////////////////////////////
Our code will compile with the standard "g++ main.cpp -o run" command.
The output is:
1 0 0
1 2 3 4 5

Conclusion

We learned how to create classes in C++ (and implement them in separate classes, as is necessary in a professional environment). We also saw how templates work for simple functions, and we extended that to classes to make a generic class that we used to implement our own data structure.

The techniques in this article are fundamental to developing larger projects, and it wasn't that hard, right? Keep practicing and read more blog posts to learn more about programming.
To see this code on github or to download it yourself, please click this link.
To see me walk through all of this code while explaining everything, please watch this video:
Like this content and want more? Feel free to look around and find another blog post that interests you. You can also contact me through one of the various social media channels. 

Twitter: @srcmake
Discord: srcmake#3644
Youtube: srcmake
Twitch: www.twitch.tv/srcmake
​Github: srcmake
References:
1. https://stackoverflow.com/questions/1111440/undefined-reference-error-for-template-method

Comments are closed.

    Author

    Hi, I'm srcmake. I play video games and develop software. 

    Pro-tip: Click the "DIRECTORY" button in the menu to find a list of blog posts.
    Metamask tip button
    License: All code and instructions are provided under the MIT License.

    Discord

    Chat with me.


    Youtube

    Watch my videos.


    Twitter

    Get the latest news.


    Twitch

    See the me code live.


    Github

    My latest projects.

Powered by Create your own unique website with customizable templates.