Click here to Skip to main content
15,860,943 members
Articles / Programming Languages / C

A Brief Primer on C++

Rate me:
Please Sign up or sign in to vote.
4.22/5 (4 votes)
25 Jul 2009CPOL8 min read 33.8K   129   22   6
An article that steps through some essential C++ concepts.

Preface

This article will step through some of the basics of C++ and object-oriented programming with the intention of using C++ in a practical application. The original C++ syntax is based on the C language. Problems, however, arose from the fact that C++ programmers would sometimes use another developer’s class library that defined the members differently in a class of the same name. This resulted in system crashes and the introduction to the concept of the namespace.

The ANSI/ISO C language is a procedurally-oriented language and as such, uses the function as the basic unit of organization. The functions contained in a C program are predefined to reside in header files. The header files do not get compiled, but rather are preprocessed. The source code is translated into native machine language into an object file. The executable is built during the linking process, where the linker copies the function definitions contained in those included header files onto the translated source code file. C++ is an object-oriented language that uses the class as its basic unit of organization. The header files therefore consist of class libraries. A class is an abstraction. For example, a class Orange may represent one box of oranges. The oranges in that box are the members. The methods used to operate on those oranges are semantically-related. The number of object members is concretely defined. The class is an abstraction to provide a conceptual view. You make a new type in C++ by declaring a class. A class is just a collection of data members – often of different types – combined with a set of functions. In C++, a header file declares those classes that contain object members. The source code files implement those methods defined in the class on the data members of that class. An object is never accessed as a whole. Access to an object by instantiating a class involves three keywords: public, private, and protected. Here is an example of declaring a class:

C++
class  Dog
{
   unsigned int itsAge;
   unsigned int itsWeigth; 
   void Bark();
}

For the beginner, <iostream> is the header file for the input and output stream to and from standard output. “cout” means console output, and “cin” mean console input. It is best to use the using namespace std; statement to indicate that you are importing the Standard Template Library. Declaring this class doesn’t allocate memory for a Dog. It just tells the compiler what a Dog is, what data members it contains (itsAge, itsWeight), and what it can do (Bark()). Now, while memory was not allocated, it does let the compiler know that how big a Dog is (i.e., how much room the compiler must set aside for each Dog that we create). In this example, if an integer (Int32) is four bytes, a Dog then is 8 bytes big: itsAge is 4 bytes and itsWeight is 4 bytes. Bark() takes up only the room required for storing information on the location of Bark(). This is a pointer to a function that can take 4 bytes on a 32 bit platform.

Public versus Private

The code above does not use any of the accessor keywords, and is therefore considered to be private by default. Private members can be accessed only within the methods of the class itself. Public members can be accessed through any object of the class. The way to use Dog so that you can access the data members is to make some of the members public:

C++
class  Dog
{
Public:
   unsigned int itsAge;
   unsigned int itsWeigth; 
   void Bark();
};

Now, we will write code that demonstrates the declaration of a Dog with public member variables:

C++
#include <iostream>
class  Dog
{
public:
    int itsAge;
    int itsWeigth; 
    void Bark();
};

int  main() {
{
    Dog Lassie;
    Lassie.itsAge = 5;  // initialize, or assign, to the member variable
    std::cout  <<  "Lassie is a dog who is  "      ;
    std::cout <<  Lassie.itsAge <<  "  years old.\n";
    return 0;
    }
}

Compiling this code results in the phrase: Lassie is a dog who is 5 years old.

When designing types, you should keep the data members of a class private. To access private data in a class, you must create public functions known as accessor methods. Use these methods to set and get the private member variables. These accessor methods are the member functions that other parts of your program call to get and set private member variables: a public accessor method is, then, a class member function used either to read (get) the value of a private class member variable or to set its value. This leads us to a standardized mechanism that is used in object-oriented programming: separation of the interface from its implementation. Accessor functions are added rather than reading data directly. This creates indirection, but this indirection means that accessor functions enable you to separate the details of how data is stored from how it is used. By using accessor functions, you can later change how the data is stored without having to rewrite any of the other functions used in your application that use the data. This is why an object is never accessed as a whole, and this is a major tenet of COM programming’s language independence: the separation of interface from its implementation hides the details of how the functionality is carried out. COM is a binary entity, and certain languages define the same function differently. If the interface definition is universal, then at the binary level, where all memory tables will appear the same, the function definitions can reside, so long as the language uses functions that return function pointers. Therefore, an accessor function provides a public interface to the private data members of the class. Examine the code below that illustrates the complete declaration of a class and the implementation of its accessor functions and one general class member function:

C++
#include <iostream>
using namespace std;
class  Dog
{
   public:
    int GetAge();
    void SetAge(int age);
    void Bark();
 
   private:
 
    int itsAge;
};

int Dog::GetAge()
{
   return itsAge;
}

void  Dog::SetAge(int age)
{
  itsAge = age;
}

void Dog::Bark()
{
     std::cout << "Bark.\n";
}

// create  a dog, set its age, have it bark, tell us its age, and then
// have it bark again
int main()
{
  Dog Lassie;
  Lassie.SetAge(5);
  Lassie.Bark();
  std::cout << "Lassie is a dog who is " ;
  std:cout << Lassie.GetAge()  << " years old.\n";
  Lassie.Bark();
  return 0;
}

The output is, of course:

Bark.
Lassie is a dog who is 5 years old.
Bark.

Adding Constructors and Destructors

A data-type, or variable, is always first declared, and then initialized. For example, ‘int Weight” declares the variable of type integer , which tells the compiler 4 bytes of storage needs to be set aside. When we initialize the variable, we assign it a value: Weight = 7;. Of course, we can both declare and initialize a variable: int Weight = 7;. You can initialize the member data of a class using a special member called a constructor. The constructor can take parameters as needed, but they cannot have a return value (not even void). The constructor is a class method with the same name as the class itself. Whenever you declare a constructor, you’ll also want to declare a destructor. Just as constructors create and initialize objects of a class, destructors clean up after your object is not being used anymore by an object user. That is, a destructor purposes to free up the resources and any memory that you might have allocated (either in the constructor or throughout the lifespan of the object). Destructors always have the same name as the class, preceded by the tilde (~): ~Dog(). Here is code that illustrates these operations:

C++
#include <iostream>        // for cout
class Dog                 // begin declaration of the class
{
public:                    // begin public section
    Dog(int initialAge);   // constructor
    ~Dog();                // destructor
    int GetAge();          // accessor function
    void SetAge(int age);  // accessor function
    void Bark();
private:                   // begin private section
    int itsAge;            // member variable
};
  

Dog::Dog(int initialAge)
{
   itsAge = initialAge;
}
  
Dog::~Dog()                 // destructor, takes no action
{
}
  
// GetAge, Public accessor function
// returns value of itsAge member
int Dog::GetAge()
{
   return itsAge;
}
  
// Definition of SetAge, public
// accessor function
void Dog::SetAge(int age)
{
   // set member variable itsAge to
   // value passed in by parameter age
   itsAge = age;
}
 
void Dog::Bark()
{
   std::cout << "Bark.\n";
}
  
int main()
{
   Dog Lassie(5);
   Lassie.Bark();
   std::cout << "Lassie is a dog who is " ;
   std::cout << Lassie.GetAge() << " years old.\n";
   Lassie.Bark();
   Lassie.SetAge(7);
   std::cout << "Now Lassie is " ;
   std::cout << Lassie.GetAge() << " years old.\n";
   return 0;
}

Output

Bark.
Lassie is a dog who is 5 years old.
Bark.
Now Lassie is 7 years old.

A Practical Application

Whether you are using Visual Studio 2005 or 2008, download the zip files, extract all of the files into a newly made folder in your project's directory, and double-click the solution file. The enclosed application can be expanded upon and changed to suit your needs. It is a console-based application that manages an entities Employee records, and provides the ability to either hire or fire an employee, or list any past, current, and present employee. Other properties like salary and ID are included. Shown below is the Employee.h header file. Since the Employee class maintains all of the information about an employee, its methods provide a way to query and change that information.

C++
// Employee.h

#include <iostream>
namespace Records {
  const int kDefaultStartingSalary = 30000;
  class Employee
  {
public:

      Employee();

      void     promote(int inRaiseAmount = 1000);
      void     demote(int inDemeritAmount = 1000);
      void     hire();     // hires or re-hires the employee
      void     fire();     // dismisses the employee
      void     display();  // outputs employee info to the console

      // Accessors and setters
      void          setFirstName(std::string inFirstName);
      std::string   getFirstName();
      void          setLastName(std::string inLastName);
      std::string   getLastName();
      void          setEmployeeNumber(int inEmployeeNumber);
      int           getEmployeeNumber();
      void          setSalary(int inNewSalary);
      int           getSalary();
      bool          getIsHired();
private: 
      std::string   mFirstName;
      std::string   mLastName;
      int           mEmployeeNumber;
      int           mSalary;
      bool          fHired;
    };
}

The Employee.h file determines the behavior of the Employee class. The Records namespace is used throughout the program for application-specific code. A number of accessors provide mechanisms to change the information about an employee or query the current information about an employee. The data members are declared as private so that other parts of the code cannot modify them directly. The accessors provide the only public way of modifying or querying these values. The Employee.cpp file illustrates the implementations of the Employee class’s methods:

C++
// Employee.cpp

#include <iostream>
#include <string>
#include "Employee.h"

using namespace std;

namespace Records {

Employee::Employee()
{
    mFirstName = "";
    mLastName = "";
    mEmployeeNumber = -1;
    mSalary = kDefaultStartingSalary;
    fHired = false;
}
void Employee::promote(int inRaiseAmount)
{
    setSalary(getSalary() + inRaiseAmount);
}

void Employee::demote(int inDemeritAmount)
{
    setSalary(getSalary() - inDemeritAmount);
}
void Employee::hire()
{
    fHired = true;
}

void Employee::fire()
{
    fHired = false;
}
void Employee::display()
{
    cout << "Employee: " << getLastName() << 
         ", " << getFirstName() << endl;
    cout << "-------------------------" << endl;
    cout << (fHired ? "Current Employee" : "Former Employee") << endl;
    cout << "Employee Number: " << getEmployeeNumber() << endl;
    cout << "Salary: $" << getSalary() << endl;
    cout << endl;
}
// Accessors and setters

void Employee::setFirstName(string inFirstName)
{
    mFirstName = inFirstName;
}

string Employee::getFirstName()
{
    return mFirstName;
}

void Employee::setLastName(string inLastName)
{
    mLastName = inLastName;
}

string Employee::getLastName()
{
    return mLastName;
}

void Employee::setEmployeeNumber(int inEmployeeNumber)
{
    mEmployeeNumber = inEmployeeNumber;
}

int Employee::getEmployeeNumber()
{
    return mEmployeeNumber;
}

void Employee::setSalary(int inSalary)
{
    mSalary = inSalary;
}

int Employee::getSalary()
{
    return mSalary;
}

bool Employee::getIsHired()
{
    return fHired;
}

}

The EmployeeTest.cpp file is used to test some of the operations. If you are confident that the Employee class works, then you, as shown, comment out the code:

C++
#include <iostream>
#include "stdafx.h"
#include "Employee.h"

using namespace std;

//int main (int argc, char** argv)
//{
//  cout << "Testing the Employee class." << endl;
//
//
//
//  emp.setFirstName("Dick");
//  emp.setLastName("Smith");
//  emp.setEmployeeNumber(71);
//  emp.setSalary(50000);
//  emp.promote();
//  emp.promote(50);
//  emp.hire();
//  emp.display();
//}

The Database class uses an array to store Employee objects. An integer called mNextSlot is used as a marker to keep track of the next unused array slot. This is not a good use of data structures because the array is fixed size -- it does not dynamically resize:

C++
// Database.h

#include <iostream>
#include "Employee.h"

namespace Records {

  const int kMaxEmployees = 100;
  const int kFirstEmployeeNumber = 1000;
class Database
{
public:
    Database();
    ~Database();

    Employee& addEmployee(std::string inFirstName, std::string inLastName);
    Employee& getEmployee(int inEmployeeNumber);
    Employee& getEmployee(std::string inFirstName, std::string inLastName);
    void        displayAll();
    void        displayCurrent();
    void        displayFormer();
protected:
    Employee    mEmployees[kMaxEmployees];
    int         mNextSlot;
    int         mNextEmployeeNumber;
};
}

Two constants are associated with the database. The maximum number of employees is a constant because the records are kept in a fixed-size array. Because the database will also take care of automatically assigning an employee number to a new employee, a constant defines where the numbering begins. The database provides an easy way to add a new employee by providing a first and last name. For convenience, this method will return a reference to the new employee. Below is the corresponding Database.cpp file:

C++
// Database.cpp

#include <iostream>
#include <stdexcept>
#include <string>
#include "Database.h"

using namespace std;

namespace Records {

  Database::Database()
  {
    mNextSlot = 0;
    mNextEmployeeNumber = kFirstEmployeeNumber;
  }
  Database::~Database()
  {
  }
  Employee& Database::addEmployee(string inFirstName, string inLastName)
  {
    if (mNextSlot >= kMaxEmployees) {
      cerr << "There is no more room to add the new employee!" << endl;
      throw exception();
    }

    Employee& theEmployee = mEmployees[mNextSlot++];
    theEmployee.setFirstName(inFirstName);
    theEmployee.setLastName(inLastName);
    theEmployee.setEmployeeNumber(mNextEmployeeNumber++);
    theEmployee.hire();

    return theEmployee;
  }
  Employee& Database::getEmployee(int inEmployeeNumber)
  {
    for (int i = 0; i < mNextSlot; i++) {
      if (mEmployees[i].getEmployeeNumber() == inEmployeeNumber) {
    return mEmployees[i];
      }
    }

    cerr << "No employee with employee number " 
         << inEmployeeNumber << endl;
    throw exception();
  }

  Employee& Database::getEmployee(string inFirstName, string inLastName)
  {
    for (int i = 0; i < mNextSlot; i++) {
      if (mEmployees[i].getFirstName() == inFirstName &&
      mEmployees[i].getLastName() == inLastName) {
    return mEmployees[i];
      }
    }

    cerr << "No match with name " << inFirstName 
         << " " << inLastName << endl;
    throw exception();
  }
  void Database::displayAll()
  {
    for (int i = 0; i < mNextSlot; i++) {
      mEmployees[i].display();
    }
  }

  void Database::displayCurrent()
  {
    for (int i = 0; i < mNextSlot; i++) {
      if (mEmployees[i].getIsHired()) {
    mEmployees[i].display();
      }
    }
  }

  void Database::displayFormer()
  {
    for (int i = 0; i < mNextSlot; i++) {
      if (!mEmployees[i].getIsHired()) {
    mEmployees[i].display();
      }
    }
  }
}

The Database class’s DatabaseTest.cpp file is shown below, again with the main body of the source code execution commented out. Classes are best tested in isolation:

C++
// DatabaseTest.cpp

#include <iostream>
#include "Database.h"

using namespace std;
using namespace Records;

/*
int main(int argc, char** argv)
{
  Database myDB;

  Employee& emp1 = myDB.addEmployee("Skippy", "Willy");
  emp1.fire();

  Employee& emp2 = myDB.addEmployee("Scott", "Tissue");
  emp2.setSalary(100000);

  Employee& emp3 = myDB.addEmployee("Jiffy", "Pop");
  emp3.setSalary(10000);
  emp3.promote();

  cout << "all employees: " << endl;
  cout << endl;
  myDB.displayAll();

  cout << endl;
  cout << "current employees: " << endl;
  cout << endl;
  myDB.displayCurrent();

  cout << endl;
  cout << "former employees: " << endl;
  cout << endl;
  myDB.displayFormer();
}
*/

The User Interface

The UserInterface.cpp file provides a menu-based display in which to work with. The main function is a loop that displays the menu, performs the selected action, then does it all again. For most actions, separate functions are defined. For simpler actions, like displaying employees, the actual code is put in the appropriate case.

C++
// UserInterface.cpp

#include <iostream>
#include <stdexcept>
#include <string>

#include "Database.h"

using namespace std;
using namespace Records;

int displayMenu();
void doHire(Database& inDB);
void doFire(Database& inDB);
void doPromote(Database& inDB);
void doDemote(Database& inDB);

int main(int argc, char** argv)
{
  Database employeeDB;
  bool done = false;

  while (!done) {
    int selection = displayMenu();

    switch (selection) {
    case 1:
      doHire(employeeDB);
      break;
    case 2:
      doFire(employeeDB);
      break;
    case 3:
      doPromote(employeeDB);
      break;
    case 4:
      employeeDB.displayAll();
      break;
    case 5:
      employeeDB.displayCurrent();
      break;
    case 6:
      employeeDB.displayFormer();
      break;
    case 0:
      done = true;
      break;
    default:
      cerr << "Unknown command." << endl;
    }
  }
}

int displayMenu()
{
  int selection;

  cout << endl;
  cout << "Employee Database" << endl;
  cout << "-----------------" << endl;
  cout << "1) Hire a new employee" << endl;
  cout << "2) Fire an employee" << endl;
  cout << "3) Promote an employee" << endl;
  cout << "4) List all employees" << endl;
  cout << "5) List all current employees" << endl;
  cout << "6) List all previous employees" << endl;
  cout << "0) Quit" << endl;
  cout << endl;
  cout << "---> ";

  cin >> selection;

  return selection;
}

void doHire(Database& inDB)
{
  string firstName;
  string lastName;

  cout << "First name? ";
  cin >> firstName;
  cout << "Last name? ";
  cin >> lastName;

  try {
    inDB.addEmployee(firstName, lastName);
  } catch (std::exception ex) { 
    cerr << "Unable to add new employee!" << endl;
  }
}

void doFire(Database& inDB)
{
  int employeeNumber;

  cout << "Employee number? ";
  cin >> employeeNumber;

  try {
    Employee& emp = inDB.getEmployee(employeeNumber);
    emp.fire();
    cout << "Employee " << employeeNumber 
         << " has been terminated." << endl;
  } catch (std::exception ex) {
    cerr << "Unable to terminate employee!" << endl;
  }
}

void doPromote(Database& inDB)
{
  int employeeNumber;
  int raiseAmount;

  cout << "Employee number? ";
  cin >> employeeNumber;

  cout << "How much of a raise? ";
  cin >> raiseAmount;

  try {
    Employee& emp = inDB.getEmployee(employeeNumber);
    emp.promote(raiseAmount);
  } catch (...) {
    cerr << "Unable to promote employee!" << endl;
  }
}

Here is a view of the console application, with its options:

Capture.JPG

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer Monroe Community
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralSources missing from the archive Pin
josef.jelinek27-Jul-09 23:13
josef.jelinek27-Jul-09 23:13 
GeneralRe: Sources missing from the archive Pin
logicchild28-Jul-09 0:11
professionallogicchild28-Jul-09 0:11 
GeneralSome possitive feedback... Pin
Rozis27-Jul-09 10:33
Rozis27-Jul-09 10:33 
GeneralRe: Some possitive feedback... Pin
logicchild28-Jul-09 0:41
professionallogicchild28-Jul-09 0:41 
GeneralTutes are still necessary ... Pin
Peter Hayward26-Jul-09 18:33
Peter Hayward26-Jul-09 18:33 
GeneralNice Article Pin
Leeland Clay26-Jul-09 5:31
Leeland Clay26-Jul-09 5:31 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.