Click here to Skip to main content
15,880,503 members
Articles / Programming Languages / C++
Article

The three major concepts of C++ - Part 1: Classes

Rate me:
Please Sign up or sign in to vote.
4.45/5 (57 votes)
25 Feb 200310 min read 283.2K   118   56
This article explains the first major concept of C++: Classes

Introduction

This article explains what classes are, what they are useful for and their syntax. Classes are one of the three major concepts of C++, that's why every C++ programmer should know what they are and what they do. The same goes for the other two major concepts (inheritance and polymorphism) which will be explained in the other two parts.

Basic knowledge of classes

Once again, classes are one of the three most important things in C++. They are often used to store data useful for a specific part in your program. For instance: suppose there is a BIG company you run, this company has information about every employee working there. This information could be stored in a class named employee, a single item of this information is called a member of employee. This is basically what classes do here, store information on a particular group (at least in this example). But a class can do more, a class can contain member functions which are functions that operate within a class that alter or do something with the members of the class.

Syntax

Suppose the class I discussed earlier, the class would be called employee. A class name is defined by the keyword class and then the name. For our employee class that would be:

class employee

Notice that there is no semicolon following employee. This is because after the class name comes the class definition. Here's our class definition (including the class name):

const int SIZE = 40;

class employee
{
private:
    char name[SIZE];
public:
    void set_name(char arg_name[SIZE])
    {
        strncpy(name, arg_name, SIZE);
    }
    void display_name(void)
    {
        cout << name;
    }
};

The first line has nothing to do with the class, it just specifies the number of characters name has as it's maximum. class employee tells the compiler the class name and that a class definition follows. Notice that the final delimiter has a semicolon after it, this is necessary to tell the compiler that the class definition ended. Now the keywords public and private are access specifiers, which I'll explain in a minute. name is a member of the class while set_name and display_name are member functions of the class. This is the BASIC class syntax, classes can and often will do more than this, this is just to explain the basic syntax. I'll get on to constructors and the inline/outline part later.

Objects

Instances of classes are called objects. You can create an instance of a class by doing the following:

classname objectname;

This will allocate the memory needed for a class to exist because when the class is defined it will not be created yet. It will be created when you declare an instance of the class, thus the name object. To create an object of employee we would do this:

employee mechanic;

Creating objects is done in main, WinMain, or any other function that is non-related to the class.

Using objects

The example above showed us how to create an object. This part of the article will show you how to use an object. We declared the object mechanic, now we want to use it to set name in our class. We can access a member or member function in the class by using the class member access operator or also called the dot operator (.). However, we cannot get direct access to private data, I'll explain more about that later on. So we'll have to use a member function. Using the dot operator we can invoke this. The syntax is as follows:

mechanic.set_name("John Doe");

This will call set_name of the object mechanic with the argument "John Doe". The following however (as said before) is NOT possible from within main or WinMain or any other function non-related to the class:

mechanic.name = "John Doe"; //wrong

This is because name was defined under a private access specifier which I'll discuss later. But this method does work if name was declared under a public access specifier.

Pointer notation

Well then what's the deal with a pointer to an object? It's practically the same except that the dot operator is being replaced by the member access operator (->). Here's an example:

employee mechanic;
employee* ptrmechanic;
ptrmechanic = &mechanic;
ptrmechanic->set_name("John Doe");

This cannot be used by objects! Only by pointers to objects.

Access specifiers

public and private are access specifiers. Simply put, they exist for the sake of security. They specify the kind of access given to the members and member functions of the class. In our class, name is a private member of employee while set_name and display_name are public member functions of employee. The access specifier public gives the entire program access to the data that follows it. While the access specifier private only gives the member functions in the class access to the data that follows it. There is one other access specifier: protected but it only becomes useful in inheritance, which will be what part 2 is all about.

Syntax of access specifiers

The syntax of access specifiers is as follows:

access_specifier:
    //access_specifier data
new_access_specifier:
    //new_access_specifier data

Note about new_access_specifier, new must not be treated as the standard keyword new in C++. Why did I put two access specifiers there? Well, it's like this: after new_access_specifier, the data that follows it has the access rules of new_access_specifier. What I'm trying to say is: the access rules of the access specifier go for the data that follows it before a new access specifier is stated. So the following access rules go in our employee class: name is private data, set_name and display_name are public data.

Tips on when which access specifiers to use

When to use private:
    //On class variables to keep them from alteration outside the class.

When to use protected:
    //On private data return functions in combination with inheritance.
    
When to use public:
    //On general member functions.

Note: these are ONLY tips and cases in which they are most used, however, you can use private, protected and public anywhere in your class (not inside functions of course), just be well aware of what you use it for.

Constructors and destructors

Constructors

One of the many cases in which the constructor will be used is when it is preferred to have the members of the class being initialized to certain values immediately when the object is declared. Constructors are functions that have no return types and have exactly the same name as the class, this must always be how a constructor is defined! Why you ask? Because a constructor has no need to return a value, and by giving it the same name as the class the compiler can directly see if it is a constructor or not. However, there is always a constructor, even if you not define one. One of the constructor's functions (functions not to be taken as a programming term) is to allocate the amount of memory needed for the class when the object is declared. This is why there is always a constructor. Constructors can take arguments though. A good example of when an argument is taken by a constructor is the copy constructor which takes an object of the same class and copies it's members' values into it's own members' values.

Destructors

Destructors are used to destroy an object at the end of the program. Like the constructor, the destructor is always there, even if you not define one. BUT when there is a written constructor, it is a general good practice to also write a destructor for it.

Syntax

The best way to explain the syntax of constructors and destructors is by changing our employee class definition:

const int SIZE = 40;

class employee
{
private:
    char name[SIZE];
public:
    //constructor, note: wrong initializtion list (array as string)
    employee() : name("Unemployed") 
    {
    }
    ~employee() //destructor
    {
    }
    void set_name(char arg_name[SIZE])
    {
        strncpy(name, arg_name, SIZE);
    }
    void display_name(void)
    {
        cout << name;
    }
};

Notice that after the employee() part in the constructor there is a colon, after that comes the so called initialization list. This is used to initialize variables with certain values (which can of course also be the arguments of a constructor). The initialization list is widely used in constructors. In our employee class the body of the constructor is empty, this does not have to be the case. You can also leave the initialization list out and put the part in the initialization list in the body, like this:

employee() //constructor
{
    strncpy(name, "Unemployed", SIZE);
}

This one will work UNLIKE the other one above, because when you're dealing with arrays you cannot specify an explicit initializer. In other words, the initialization list will not work on arrays treated as strings. I put it in anyway because I wanted to show you the basic use of the initialization list.

Syntax of the initialization list

: member(value), member(value) etc.

The member will be initialized with value, this can of course also be a variable or the return value of a function. The basic point is that the member will be initialized with the value between parentheses.

The destructor

The destructor destroys everything created by the class automatically, you can say it was a last will of the class. You do not have to put any delete keywords in it, except if you allocated any memory with the new keyword. For everything else it does the work for you. It has the same name as the class preceded by a tilde (~). Unlike the constructor, the destructor takes no arguments. That sounds logical because you would never need arguments when everything is destroyed. However, the function body does not have to be empty in a destructor. Why you ask? Well there is one good reason why, for example when you want to debug your program you could state something like this:

~employee() //destructor
{
    cout << "Destructor called.";
    
    /*the following would be required IF the variable name
      was allocated with the new keyword*/
    delete[] name;
}

There is always only one destructor in an entire class, unlike the constructors which there can be more of.

Argumented constructors

When a constructor is argumented, the syntax for the object declaration changes to:

employee(char arg_name[SIZE]) //constructor
{
    strncpy(name, arg_name, SIZE);
}

employee mechanic; //wrong object declaration
employee mechanic("John Doe"); //right object declaration

This is one useful method because now the set_name function wouldn't be necessary anymore.

Multiple constructors

You can also define multiple constructors for use in one class, like this:

employee() //no argument constructor
{
    strncpy(name, "Unemployed", SIZE);
}
employee(char arg_name) //one argument constructor
{
    strncpy(name, arg_name, SIZE);
}

You can also define multiple constructors which have the same amount of arguments, however, you cannot define multiple constructors that take the same types of arguments. The compiler simply wouldn't know which constructor to call. This way you can choose how to declare your object. The methods showed in the Argumented constructors part will now both work.

Inline and outline member functions

Note: the following part assumes you know what inline/outline means and what it does. Functions defined within a class (as in all the examples so far) are inline by default! As a matter of fact, the compiler makes it inline for you, whether you want to, or not. This is a disadvantage if you have a short member function that is called many times, or a very complex member function. There is however one solution to this, using the scope resolution operator. Consider our employee class: the member function display_name may be called many times. It is not crucial when this member function is defined inside the class, but it takes up a lot of memory because it's automatically defined as inline by the compiler. Here's how we solve this:

const int SIZE = 40;

class employee
{
private:
    char name[SIZE];
public:
    employee() //constructor
    {
        strncpy(name, "Unemployed", SIZE);
    }
    ~employee() //destructor
    {
    }
    void set_name(char[SIZE]); //function declarations only
    void display_name(void);
};

// the :: is the scope resolution operator
void employee::set_name(char arg_name[SIZE]) 
{
    strncpy(name, arg_name, SIZE);
}
void employee::display_name(void)
{
    cout << name;
}

Now the functions are not inline because they are defined outside the class and only declared within it. However, do watch out for the following: if the function is declared inside the class using the keyword inline and defined outside the class, the function still is inline, but doing this is wasting time because this is the same as declaring and defining the function inside the class.

The scope resolution operator

The scope resolution operator is used to incorporate the member function's name with the class name, or else the compiler won't have a single clue of which class the function was a member of. So it uses the scope resolution operator to bind these names, this way the compiler knows in which class to look for the declaration of the member function. However, from within main, WinMain, or any other non-related function of the class, the scope resolution operator cannot be used to call upon the member function of a class. For our employee example:

employee::display_name(); // from within main would do no good

To do this generates a compile error because the compiler doesn't know with which object to access the member function. Just use the dot operator or the member access operator for pointers.

Final word

Well, I guess that wraps it up. This was the first major concept in C++, there will follow two more articles on the other two: inheritance and polymorphism.

History

  • Article posted on 02/26/03

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here



Comments and Discussions

 
GeneralRe: virtual destructors Pin
Joel Matthias5-Mar-03 6:20
Joel Matthias5-Mar-03 6:20 
GeneralRe: virtual destructors Pin
Christian Graus5-Mar-03 9:25
protectorChristian Graus5-Mar-03 9:25 
GeneralRe: virtual destructors Pin
Joel Matthias5-Mar-03 10:02
Joel Matthias5-Mar-03 10:02 
GeneralRe: virtual destructors Pin
TW1-Dec-03 4:03
TW1-Dec-03 4:03 
GeneralRe: virtual destructors Pin
TW1-Dec-03 4:10
TW1-Dec-03 4:10 
GeneralRe: virtual destructors Pin
TW1-Dec-03 3:59
TW1-Dec-03 3:59 
GeneralRe: virtual destructors Pin
Jim Crafton26-Feb-03 10:45
Jim Crafton26-Feb-03 10:45 
GeneralRe: virtual destructors Pin
dog_spawn26-Feb-03 11:10
dog_spawn26-Feb-03 11:10 
'undefined' means weird crash for those new to C++ Smile | :)

They're the kind of bugs that can take ages to find. I know this because I was caught out by this when I was learning.
GeneralRe: virtual destructors Pin
DevHead26-Feb-03 10:42
DevHead26-Feb-03 10:42 
GeneralRe: virtual destructors Pin
dog_spawn26-Feb-03 11:08
dog_spawn26-Feb-03 11:08 
GeneralRe: virtual destructors Pin
DevHead26-Feb-03 11:28
DevHead26-Feb-03 11:28 
GeneralRe: virtual destructors Pin
dog_spawn26-Feb-03 11:42
dog_spawn26-Feb-03 11:42 
GeneralRe: virtual destructors Pin
DevHead26-Feb-03 11:48
DevHead26-Feb-03 11:48 
GeneralRe: virtual destructors Pin
Martin Holzherr26-Feb-03 21:58
Martin Holzherr26-Feb-03 21:58 
GeneralRe: virtual destructors Pin
Joaquín M López Muñoz27-Feb-03 2:32
Joaquín M López Muñoz27-Feb-03 2:32 
GeneralRe: virtual destructors Pin
Alvaro Mendez5-Mar-03 5:32
Alvaro Mendez5-Mar-03 5:32 

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.