First Bytes Home

C++ Programming Tutorial


Section 5: Building Objects

Sometimes we want to group different kinds of data together. For example, let's think about a bank account. What kind of information does your bank account store about you? Most important are your bank account number (an integer) and your account balance (a float). It also stores your name and address (both strings). Often in programming, we want to group related data like this together in a single construct, and we can do that with objects.

A class defines an object type, the variables inside the object (such as bank account number and balance), and functions you can perform on the object (such as deposit() and withdraw()). So we begin by defining a class.

The first step is easy enough: type the keyword class, and then choose a class name beginning with an uppercase letter. For our bank account example, we'll surprise everyone with

class BankAccount { 

};

You'll notice the class definition will be encompassed by curly braces and has a trailing semi-colon. Now we need to add some variables and functions.

Let's start with the member variables. Variables inside a class are called member variables, so the member variables for this class are the account number, account balance, your name, and your address. Adding these to our skeleton definition we get the following:

class BankAccount {
  public:
     int accountNumber;
     float accountBalance;
     string name;
     string address;
};

The public keyword indicates that the member variables (and functions—we'll add those later) that follow are available to the rest of the program to use. You can also declare a class's variables and functions to be private (we'll also cover this later). Notice, too, that the variables are created the same as you've done previously, by declaring the type and giving it a descriptive name.

In order to construct a BankAccount object, we first need to define a constructor. The constructor is a piece of code that executes when a new object of this class is created. A constructor is defined in the the same way as a fuction; the only difference is the name of the constructor must match the name of the class, and the constructor has no return type. Inside the constructor, we want to set initial values for any member variables we have, otherwise our program may not work as we intend.

For a brand new BankAccount, when we don't yet know anything about the data that will be put into it, default values of zeroes and empty strings will be fine; we can always set them to something else later. Thus, our BankAccount constructor is defined as follows:

class BankAccount {
  public:
      int accountNumber;
      float accountBalance;
      string name;
      string address;

      BankAccount() {
          accountNumber = 0;
          accountBalance = 0.0;
          name = "";
          address = "";
      }
};

We have now defined a basic data structure that holds bank account information. You can write a program that creates a BankAccount object and sets the values of its fields. Notice the class must be declared before the main function where we create our object.

#include <iostream>
using namespace std;

class BankAccount {
  public:
     int accountNumber;
     float accountBalance;
     string name;
     string address;

      BankAccount() {
          accountNumber = 0;
          accountBalance = 0.0;
          name = "";
          address = "";
      }
};


int main() {
     BankAccount alisonBankAccount; //make a bankAccount for Alison
     alisonBankAccount.accountNumber = 14538;  //assign the account a number
     alisonBankAccount.accountBalance = 1000000.01;  //give Alison some money
     alisonBankAccount.name = "Alison Norman";  //assign name and address
     alisonBankAccount.address = "1600 Pennsylvania Avenue NW Washington, DC 20500";
}

Look at the first line in main. We've created a BankAccount object called alisonBankAccount and it has been initialized using the default constructor.

We use dot notation to access and set the public variables of an object, as seen in the remaining lines in main. We first write the variable name of the object (alisonBankAccount), a period, and then the name of the field we want (e.g. accountNumber). From here, we see that we can create multiple different BankAccounts and populate them all with different balances and information. For example, let's say that in addition to Alison's bank account, we'll make an account for Bob Bobson. Here's the same code as above, but now we've added a second instance of the BankAccount class:

//make two different bank accounts
BankAccount alisonBankAccount;
BankAccount bobBankAccount;

//assign two different account numbers
alisonBankAccount.accountNumber = 14538;  //assign the account a number
bobBankAccount.accountNumber = 54324;

//assign different account balances
alisonBankAccount.accountBalance = 1000000.01;  
bobBankAccount.accountBalance = 2.52;

//give Bob some money to get out of debt
//i.e. add 300.95 to the current value of accountBalance
bobBankAccount.accountBalance += 300.95; 

cout << "Bob has $" << bobBankAccount.accountBalance << ".  Yay Bob!\n";

We can give Alison and Bob's BankAccount objects completely different information, and we can also modify the values that the fields hold. Notice how Bob made a timely deposit with the line "bobBankAccount.accountBalance += 300.95;". The "+=" operator means that you increase the value of the variable to the left of the "+=" by the value on the right side of the "+=" and save it as the same variable. In other words, the following two lines are equivalent:

bobBankAccount.accountBalance += 300.95;
bobBankAccount.accountBalance = bobBankAccount.accountBalance + 300.95;
These objects do a great job of cleaning up our code—consider having to keep track of Alison and Bob's bank account info without them and what happens when we want to add a third bank account—all those variables add up! Classes and objects are also useful in other ways, too.

There are a number of reasons not to have public member variables, not the least of which is how easy it is for a programmer (you!) to shoot themselves in the foot with one. What if someone sets the accountBalance to a negative number? What does a negative account balance mean—is it even a valid value? A good rule of thumb is to make the things necessary for the inner workings of the class private and make public the things relevant to what the rest of the program is doing. We'll talk more about what that means for our BankAccount class in a moment. In the mean time, let's go ahead and declare our member variables as private, as shown below.

class BankAccount {
  public:
      BankAccount() {
          accountNumber = 0;
          accountBalance = 0.0;
          name = "";
          address = "";
      }
  private:
     int accountNumber;
     float accountBalance;
     string name;
     string address;
};

Private fields can only be accessed by instances of their containing class—in our case, our BankAccount objects. This means that main can no longer say, Hollywood hacker style,

alisonBankAccount.accountBalance = 9999999999;
We can't even access the field to see what its current value is.

Our object is actually fairly useless right now. We left the constructor public, so we can create new objects, but all our data is inaccessible from outside the class because it's declared private. This is where member functions come in. Remember classes have both member variables and member functions. The variables hold the object data, and the functions define actions that can be performed on or with those variables.

Consider what behavior a BankAccount should exhibit. What kinds of actions do we want to be able to take on it?

People need to be able to deposit to and withdraw from their accounts, as well as see their current account balance. The latter we can solve with a simple getAccountBalance function that just returns the value stored in accountBalance. The deposit and withdraw operations are a little more complex; both modify the account balance by a given amount. What might the deposit function look like? Would it take any arguments? What kind? Does it need to return anything?

class BankAccount {
  public:
     float getAccountBalance() {
 
     }
     void deposit(float amount) {
 
     }
      BankAccount() {
          accountNumber = 0;
          accountBalance = 0.0;
          name = "";
          address = "";
      }
  private:
     int accountNumber;
     float accountBalance;
     string name;
     string address;
};
Everything under the public keyword and above the private keyword is our class's public members, and everything below the private keyword is the class's private members. We want both functions to be public; they are the rest of the program's interface with our BankAccount data.

deposit will take a float as an argument, which is the amount being deposited, and update the accountBalance field appropriately. It doesn't need to return anything. Since this is a member function, we write its instructions inside the class definition, like this:

#include <iostream>
using namespace std;

class BankAccount {
  public:
     float getAccountBalance() {
          return accountBalance;
     }

     void deposit(float amount) {
          accountBalance += amount;
     }
      BankAccount() {
          accountNumber = 0;
          accountBalance = 0.0;
          name = "";
          address = "";
      }
  private:
     int accountNumber;
     float accountBalance;
     string name;
     string address;
};
Now when we want to deposit money in main, we won't need to type:
 bobBankAccount.accountBalance += 300.95;
Instead, we'll be able to say:
 bobBankAccount.deposit(300.95);
Now our main looks like this:
int main(){

     BankAccount alisonBankAccount;
     BankAccount bobBankAccount;
     bobBankAccount.deposit(300.95);
}

There are a few new things to take note of.

Consider the usefulness of using public functions to modify private data, as opposed to leaving the data public. What if we wanted to apply the same set of checks to make sure the account balance is always updated properly—for example, checking for overdrafts or account deposit limits. With our data safely hidden behind the private wall and the use of functions, we can say with confidence our bank account balances are correct.

Task 5.1:

Add another bank account to the example above. Deposit $500 dollars into Alison's account, then withdraw $400 from Bob's account and place it in the account you created. Then print out the balances from all three accounts. (You'll need to write the withdraw function. How is it similar to the deposit function? How is it different? What happens if you attempt to withdraw an amount greater than your balance?)

Task 5.2:

Create a super hero class. The fields will include the superhero's name, arch-nemesis, weakness, and sidekick (these are all strings). Add some extra fields of your own. Then make some instances of the superhero class using your favorite super heroes for inspiration.


Congratulations, you are well on your way to mastering the complex subject that is object-oriented programming! Now, let's move on to loops.