Saturday, January 18, 2014

C++ Programming (from the book Thinking in C++)

Identifier names

The first letter of an identifier is only capitalized if that identifier is a class. If it is a function or variable, then the first letter is lowercase. The rest of the identifier consists of one or more words, run together but distinguished by capitalizing each word.

So a class looks like this:
class FrenchVanilla : public IceCream {

an object identifier looks like this:
FrenchVanilla myIceCreamCone(3);

and a function looks like this:
void eatIceCreamCone();
(for either a member function or a regular function).

The one exception is for compile-time constants (const or #define), in which all of the letters in the identifier are uppercase.

The value of the style is that capitalization has meaning – you can see from the first letter whether you’re talking about a class or an object/method. This is especially useful when static class members are accessed.

Order of header inclusion
Headers are included in order from “the most specific to the most general.” That is, any header files in the local directory are included first, then any of my own “tool” headers, such as require.h, then any third-party library headers, then the Standard C++ Library headers, and finally the C library headers.

If the order of header inclusion goes “from most specific to most general,” then it’s more likely that if your header doesn't parse by itself, you’ll find out about it sooner and prevent annoyances down the road.

Include guards on header files
Include guards are always used inside header files to prevent multiple inclusion of a header file during the compilation of a single .cpp file. The include guards are implemented using a preprocessor #define and checking to see that a name hasn’t already been defined. The name used for the guard is based on the
name of the header file, with all letters of the file name uppercase and replacing the ‘.’ with an underscore. For example:
// IncludeGuard.h
#ifndef INCLUDEGUARD_H
#define INCLUDEGUARD_H
// Body of header file here...
#endif // INCLUDEGUARD_H

Use of namespaces
In header files, any “pollution” of the namespace in which the header is included must be scrupulously avoided. That is, if you change the namespace outside of a function or class, you will cause that change to occur for any file that includes your header, resulting in all kinds of problems. No using declarations of any
kind are allowed outside of function definitions, and no global using directives are allowed in header files.

Accessor Method
As a general rule of design, 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.

A public accessor method is a class member function used either to read the value of a private class member variable or to set its value. This practice enables yo to separate the details of how the data is stored from how it is used. You can later change how the data is stored without having to rewrite any of the other functions in your programs that use the data.

const Member Functions
If you declare a class method const, you are promising that the method won't change the value of any of the members of the class. It is good programming practice to declare as many methods to be const as possible.

Class declarations and method definitions
Each function that you declare for your class must have a definition (the function implementation). The convention is to put the declaration into what is called a header file: most of the time, clients of your class don't care about the implementation specifics, and reading the header file tells them everything  they need to know.

The declaration of a class is called its interface because it tells the user how to interact with the class. The function definition tells the compiler how the function works.


Pointers
The address-of operator (&) returns the address of an object in memory.
Pointers are used mainly for three tasks:
1. Managing data on the free store
2. Accessing class member data and functions
3. Passing variables by reference to functions

The free store (heap) is not cleaned until your program ends, and it is your responsibility to free any memory that you've reserved when you are done with it. The advantage to the free store is that the memory you reserve remains available until you explicitly state you are done with it by freeing it.

You allocate memory on the free store in C++ by using the new keyword. new is followed by the type of the object that you want to allocate. The return value from new is a memory address.
int pPointer= new int;

Remember to delete a pointer after you are done using it. For every time in your program that you call new, there should be a call to delete.

When you call delete on a pointer to an object on the free store, that object's destructor is called before the memory is released.

Objects on the free store persist after the return of a function. The capacity to store objects on the free store enables you to decide at runtime how many objects you need, instead of having to declare this in advance.

The this Pointer
Each class member function has a hidden parameter: the this pointer, pointing to "this" individual object.  A pointer to the object that holds the function.

If you declare a pointer to a const object, the only methods that you can call with that pointer are const methods.

Protect objects passed by reference with const if they should not be changed. Set pointers to nullptr rather than leaving them uninitialized or dangling.

The C++ clients of classes and functions can rely on the header file to tell all that is needed: it acts as the interface to the class or function. The actual implementation is hidden from the client. This enables the programmer to focus on the problem at hand and to use the class or function without concern for how it works.


Passing by Reference
Passing by value is like giving a museum a photograph of your masterpiece instead of the real thing. Passing by reference is like sending your home address to the museum and inviting guests to come over and look at the real thing. The solution is to pass a pointer to a constant object OR a reference to a constant object. Doing so prevents calling any non-const method on it, and thus protects the object from change.

Whenever possible, pass parameters by reference. Don't use pointers if references will work.
Whenever possible, use const to protect references and pointers.

It is safer to build your functions so that the delete the memory they create.


Arrays
The American National Standards Institute (ANSI) standard declares the scope of variables in the for loop only to the block of the for loop itself.

Think of the index of an array as the offset.

In C++, an array name is a constant pointer to the first element of the array. In the declaration
Cat Family[500];
Family is a pointer to &Family[0], which is the address of the first element of the array Family. For all practical purposes, you can treat the pointer to an array as the name of the array. The one thing you will need to do, however, is to free the memory you allocated in setting up the array. In C++, arrays are no more than special cases of pointers.

Deleting Family automatically returns all the memory set aside for the array if you use the delete with the [] operator. By including the square brackets, the compiler is smart enough to destroy each object in the array and to return its memory to the free store. If you leave the brackets off, only the first object in the array is deleted.

When you create an item on the heap by using new, you always delete that item and free its memory with delete. Similarly, when you create an array by using new <class>[size], you delete that array and free all its memory with delete []. The brackets signal the compiler that this array is being deleted.

The biggest advantage of being able to allocate arrays on the heap is that you determine the size of the array at runtime and  then allocate it.






No comments:

Post a Comment