Table of Contents
Overview:
- Terms to know: File guards, objects, Object Oriented Programming, encapsulation, loose coupling, high cohesion, structs, members,
- New code:
Creating a file guard in a .h file:
#ifndef _LABEL #define _LABEL // code goes here #endif
Declaring a struct:
struct NAME { int MEMBERVAR1; float MEMBERVAR2; string MEMBERVAR3; };- Declaring a variable whose data type is a struct:
STRUCTNAME STRUCTVAR; - Accessing members of the struct:
STRUCTVAR.MEMBERVAR1 = 123;cout << STRUCTVAR.MEMBERVAR2 << endl;
1. Introduction to objects
Programming paradigms (pronounced "pair-uh-dimes") are ways we can classify different programming styles. Some programming languages support multiple paradigm styles and some are restricted to one style of coding. Over the decades different paradigms have been developed and evolved over time. Object Oriented Programming is one of the most common styles of programming these days, and is a big part of how C++, C#, and Java-based programs are designed.
1.1. What are "Objects"?
Defining classes and structs in our programs are a way that we can create our own data types for our variables. When we define our own structures, we can create them with internal variables and functions available to them. The variables we create, whose data types come from a defined class or struct, is known as an object.
Design-wise, the idea is to take real-world objects and find a way to represent them in a computer program as an object that has attributes (variables) and functionality (functions).
1.2. OOP design ideals
The concept of Object Oriented Programming is creating a design that is easy to maintain over time. In particular, there are some core design goals behind OOP, including:
- Encapsulation: Giving the user / other programmers an “interface” \ to
interact with the objects, hiding the inner-workings within the class.
- Certain functions are made public, which other programmers can use to interface with the object.
- The other programmers don't need to worry about the inner-workings of the object in order to use it.
- The developer of the class can modify how the internals work without breaking the public interface.
- Helps protect the data within the class from being accessed or modified by external things that shouldn't have access to it.
- Loose coupling: Ideally, different objects in a program shouldn't have their functionality tied to other objects too closely; we want to reduce inter-dependence between objects. When objects are more independent from each other, we say they are loosely coupled.
- High cohesion: When we design our objects, we shouldn't just throw everything and the kitchen sink into one object. To design an object with high cohesion means that everything inside the object belongs to that object - reduce the clutter.
Review 1. What does it mean for our objects' designs to be "High Cohesion"?
2. Making our own data type with struct
Structs are a way we can group related information together into one data type.
For example, let's say we were writing a program for a restaurant, and an item on the menu would have a name, a price, a calorie count, and a "is it vegetarian?" signifier. We could declare these all as separate variables:
string food1_name; float food1_price; int food1_calories; bool food1_is_veggie;
But there isn't really anything in the program that says these variables are related, except that we as humans have given these variables similar prefixes in their names.
A better design would be to create a new datatype called MenuItem (or something similar), and the MenuItem will contain these four variables within it. We're basically making a variable data type that can contain multiple variables!
First we create the struct, which should go in its own .h file:
Example: MenuItem.h
struct MenuItem { string name; float price; int calories; bool isVeggie; };
And then we can declare variables of this type in our program:
Example: Within int main()
MenuItem food1; food1.name = "Bean Burrito"; food1.price = 1.99; food1.calories = 350; food1.isVeggie = true; MenuItem food2; food2.name = "Crunchy Taco"; food2.price = 1.99; food2.calories = 170; food2.isVeggie = false;
Review 2. Those separate variables with the same prefix to indicate a relationship are (○ beautiful / ○ ugly)
Review 3. If we were storing multiple food items and using this approach for each, what might be a downside to the design?
- ○ It's too much typing.
- ○ Our programs should have no more than 3 variables.
- ○ There is nothing that requires every food to have the same set of variables.
Review 4. So, Creating a struct to group like-things together and make a new data type. (○ cool! / ○ terrible!)
3. Best practices: Multiple files in C++ programs
When creating a struct, we should create a new source code file in our project. Usually the name of the file will be the same name as the struct, with a .h at the end. This is a header file and declarations for structs (and functions and classes later) will go in these types of files.
Something you need to require in your .h files that isn't needed for your .cpp files are file guards. These prevent the compiler from reading the file more than once. If it reads it multiple times, it will think you're redeclaring things over and over again. Your .h files should always look like this:
Example: Struct .h file
#ifndef _MYFILE_H #define _MYFILE_H // put code here #endif
Where the "_MYFILE_H" should be changed to something unique in each file -
usually, the name of your file.
Review 5. In standard C++ code, it is better to move our struct declaration to a header (.h) file… (○ true / ○ false)
Review 6. File guards are needed in .h files because…
- ○ It makes sure the contents of the .h file don't bleed into your main() function.
- ○ It protects the file from invaders.
- ○ It prevents the compiler from reading the same file twice and thinking that there's duplicate code.
Review 7. A struct can be declared above int main() and the
program will still compile and run… (○ true / ○ false)
4. Creating our own structs
4.1. Declaring a struct
A basic struct declaration is of the form…
#ifndef _MYFILE_H
#define _MYFILE_H
struct STRUCTNAME
{
// member variables
int MEMBER1;
string MEMBER2;
float MEMBER3;
};
#endif
Note that the closing curly-brace } must end with a ; otherwise the
compiler will give you errors.
You can name your struct anything you'd please but it has to abide by the C++ naming rules (no spaces, no keywords, can contain letters, numbers, and underscores).
Any variables we declare within the struct are known as member variables - they are members of that struct.
Any new variables you declare whose data type is this struct will have its own copy of each of these variables.
4.2. Declaring object variables
A object is a type of variable whose data type is a struct (or class). To declare a variable whose data type is a struct, it looks like any normal variable declaration:
DATATYPE VARIABLE;
4.3. Accessing member variables
Our struct object variables each have their own member variables, which
we access via the dot operator .. We can assign values to it, our read its
value, similar to any normal variable.
VARIABLE.MEMBER1 = 10; VARIABLE.MEMBER2 = "ASDF"; VARIABLE.MEMBER3 = 2.99; cout << VARIABLE.MEMBER1 << endl;
4.4. Displaying member variables
When we have a struct declared and an object-variable created…
Book myBook;
… We won't be able to use cout on the object…
cout << myBook << endl;
… (Unless we overload operators, but that's for a future class!) Instead, remember that you have to output each member variable in the way you wish, because C++ won't know how you want to display the contents of a struct!…
cout << "Title: " << myBook.title << endl << "Author: " << myBook.author << endl;
Review 8. What would be an appropriate declaration of a struct meant to represent a vehicle, storing the vehicles's manufacturer and year within?
Review 9. Given a struct named MenuItem, to declare a variable
named food2 of this type, we would write…
Review 10. If a struct Teacher is declared and an object-variable is created:
Teacher cs200Teacher;
Does the following command work?
cout << cs200Teacher << endl;
- ○ No, C++ doesn't know how to cout a "Teacher" object we've created.
- ○ Yes, C++ will automatically recognize the Teacher struct's member variables and display them in order.
Review 11. If we declared a Course object like: Course cs200;
How would we assign "cs" to the course department and "200" to the course code?
5. Example: Fraction struct
First we would declare our Fraction struct within Fraction.h:
#ifndef _FRACTION_H #define _FRACTION_H struct Fraction { int num; int denom; }; #endif
Within main(), we could then create Fraction variables and work with them:
#include "Fraction.h" int main() { Fraction frac1, frac2, frac3; cout << "FIRST FRACTION" << endl; cout << "* Enter numerator: "; cin >> frac1.num; cout << "* Enter denominator: "; cin >> frac1.denom; cout << endl; cout << "SECOND FRACTION" << endl; cout << "* Enter numerator: "; cin >> frac2.num; cout << "* Enter denominator: "; cin >> frac2.denom; frac3.num = frac1.num * frac2.num; frac3.denom = frac1.denom * frac2.denom; cout << endl; cout << "PRODUCT: " << frac3.num << "/" << frac3.denom << endl; return 0; }
6. Composition: Structs with other Structs as member objects
In addition to creating structs with normal variables, we can also create struct-object-variables as members of another struct.
Let's say we have a Teacher struct and a Course struct, for each Course we want to store the teacher of that course…
Teacher.h:
#ifndef _TEACHER_H #define _TEACHER_H #include <string> using namespace std; struct Teacher { string name; string email; }; #endif
Course.h:
#ifndef _COURSE_H #define _COURSE_H #include "Teacher.h" #include <string> using namespace std; struct Course { Teacher teacher; string department; int code; int hours; }; #endif