Tuesday, October 30, 2012

Stroustrups C++ notes - (Chapter 10 - Classes)

People often ask me "What is the best book to learn C++" and my answer usually is Stroutrup. I don't know why but many people are afraid of this book. I have done some analysis by asking question to many of my friends who are afraid of Stroustrups book and found some reason why people are afraid fo this book.
1. As a beginner of C++ this book gives a feeling that I you are digressing a lot from the topics you are reading.
2. Many useful and very very important facts are given as one liners.
3. The example code and theory are mixed in a very complex manner.

But still I would like to say again, Stroustrup's third edition is the best C++ book because of many reasons.
1. It covers all the topics of C++.
2. You Will find answer to all the C++ questions in this book.
3. The explanation is awesome (even though it appears rhetoric).

SO I thought why not give the readers a chance to study the crux of stroustrup. so IF YOU KNOW C++ and you are already PROGRAMMING IN C++ and you wanna read this book but you are afraid to open it because of a some reason then this article is for you. In this article and my next few articles I will be publishing my personal notes from this book which could make your life a little easy.

Mostly These notes are just theory If you have any doubts or you want any code snippet to be attached with any explanation please feel free to ask. SO let us begin.

Chapter 10: Classes
The aim of the C++ class concept is to provide the programmer with a tool for creating new types that can be used as conveniently as the built in types. In addition, derived classes and templates provide ways of organizing related classes that allow the programmer to take advantage of their relationships.

The fundamental idea in defining a new type is to separate the incidental details of the implementation (e.g., the layout of the data used to store an object of the type) from the properties essential to the correct use of it (e.g., the complete list of functions that can access the data). Such a separation is best expressed by channeling all uses of the data structure and internal housekeeping routines through a specific interface.



Classes
Functions declared within a class definition (a struct is a kind of class) are called member functions and can be invoked only for a specific variable of the appropriate type using the standard syntax for structure member access.

Because different structures can have member functions with the same name, we must specify the structure name when defining a member function. (While providing the function definition)

In a member function, member names can be used without explicit reference to an object. In that case, the name refers to that member of the object for which the function was invoked.

The construct

class X{ ... };

is called a class definition because it defines a new type. For historical reasons, a class definition is often referred to as a class declaration. Also, like declarations that are not definitions, a class definition can be replicated in different source files using #include without violating the one definition rule.

Access Control
The public label separates the class body into two parts. The names in the first, private, part can be used only by member functions. The second, public, part constitutes the public interface to objects of the class. A struct is simply a class whose members are public by default. However, nonmember functions are barred from using private members.

In particular, if we change the representation of a class, we need only change the member functions to take advantage of the new representation. User code directly depends only on the public interface and need not be rewritten (although it may need to be recompiled). Another advantage is that a potential user need examine only the definition of the member functions in order to learn to use a class.

The protection of private data relies on restriction of the use of the class member names. It can therefore be circumvented by address manipulation and explicit type conversion. But this, of course, is cheating. C++ protects against accident rather than deliberate circumvention (fraud). Only hardware can protect against malicious use of a general purpose language, and even that is hard to do in realistic systems.

Constructors
The use of functions such as init() to provide initialization for class objects is inelegant and error prone. Because it is nowhere stated that an object must be initialized, a programmer can forget to do so – or do so twice (often with equally disastrous results). A better approach is to allow the programmer to declare a function with the explicit purpose of initializing objects. Because such a function constructs values of a given type, it is called a constructor. A constructor is recognized by having the same name as the class itself.

When a class has a constructor, all objects of that class will be initialized. If the constructor requires arguments, these arguments must be supplied.

It is often nice to provide several ways of initializing a class object. This can be done by providing several constructors. Constructors obey the same overloading rules as do other functions. As long as the constructors differ sufficiently in their argument types, the compiler can select the correct one for each use.

When designing a class, a programmer is always tempted to add features just because somebody might want them. It takes more thought to carefully decide what features are really needed and to include only those. However, that extra thought typically leads to smaller and more comprehensible programs. One way of reducing the number of related functions is to use default arguments.

Static Members
Fortunately, we can get the convenience without the encumbrance of a publicly accessible global variable. A variable that is part of a class, yet is not part of an object of that class, is called a static member. There is exactly one copy of a static member instead of one copy per object, as for ordinary non static members. Similarly, a function that needs access to members of a class, yet doesn’t need to be invoked for a particular object, is called a static member function.

Copying Class Objects
By default, class objects can be copied. In particular, a class object can be initialized with a copy of another object of the same class. This can be done even where constructors have been declared.
By default, the copy of a class object is a copy of each member. If that default is not the behavior wanted for a class X , a more appropriate behavior can be provided by defining a copy constructor,

X ::X (c o n s t X &).

Similarly, class objects can by default be copied by assignment. Again, the default semantics is memberwise copy. If that is not the right choice for a class X , the user can define an appropriate assignment operator.

Constant Member Functions
Writing const after the () argument list in the function declarations indicates that these functions do not modify the state of a class.

A const member function can be invoked for both const and nonconst objects, whereas a nonconst member function can be invoked only for nonconst objects.

Self Reference
it is often useful to return a reference to the updated object so that the operations can be chained. Each (nonstatic) member function knows what object it was invoked for and can explictly refer to it.
The expression *t h i s refers to the object for which a member function is invoked.

In a nonstatic member function, the keyword this is a pointer to the object for which the function was invoked. In a nonconst member function of class X , the type of this is X *const . The const makes it clear that the user is not supposed to change the value of this. In a const member function of class X , the type of this is const X *const to prevent modification of the object itself.

Most uses of this are implicit. In particular, every reference to a nonstatic member from within a class relies on an implicit use of this to get the member of the appropriate object.


Physical and Logical Constness
Occasionally, a member function is logically const , but it still needs to change the value of a member. To a user, the function appears not to change the state of its object. However, some detail that the user cannot directly observe is updated. This is often called logical constness.

Mutable
The explicit type conversion ‘‘casting away c o n s t ’’ and its consequent implementation dependent behavior can be avoided by declaring the data involved in the cache management to be m u t a b l e.

The storage specifier m u t a b l e specifies that a member should be stored in a way that allows updating – even when it is a member of a c o n s t object. In other words, m u t a b l e means ‘‘can never be c o n s t .’’

Declaring members m u t a b l e is most appropriate when (only) part of a representation is allowed to change. If most of an object changes while the object remains logically c o n s t , it is often better to place the changing data in a separate object and access it indirectly.

Structures and Classes
By definition, a s t r u c t is a class in which members are by default public; that is,
s t r u c t s { ...
is simply shorthand for
c l a s s s { p u b l i c : ...
The access specifier p r i v a t e : can be used to say that the members following are private, just as
p u b l i c : says that the members following are public.

prefer to use s t r u c t for classes that have all data public. I think of such classes as ‘‘not quite proper types, just data structures.’’ Constructors and access functions can be quite useful even for such structures, but as a shorthand rather than guarantors of properties of the type.

It is not a requirement to declare data first in a class. In fact, it often makes sense to place data members last to emphasize the functions providing the public user interface.

InClass Function Definitions
A member function defined within the class definition – rather than simply declared there – is taken to be an inline member function. That is, inclass definition of member functions is for small, frequently used functions. Like the class definition it is part of, a member function defined inclass can be replicated in several translation units using #i n c l u d e . Like the class itself, its meaning must be the same wherever it is used.
The style of placing the definition of data members last in a class can lead to a minor problem with public inline functions that refer to the representation.

Efficient User Defined Types
C++ and other programming languages directly support a few of these abstractions. However, most are not, and cannot be, supported directly because there are too many of them. Furthermore, the designer of a general purpose programming language cannot foresee the detailed needs of every application. Consequently, mechanisms must be provided for the user to define small concrete types. Such types are called concrete types or concrete classes to distinguish them from abstract classes and classes in class hierarchies.

It was an explicit aim of C++ to support the definition and efficient use of such user defined data types very well. They are a foundation of elegant programming. As usual, the simple and mundane is statistically far more significant than the complicated and sophisticated.

This set of operations is fairly typical for a user defined type:
[1] A constructor specifying how objects/variables of the type are to be initialized.
[2] A set of functions allowing a user to examine a CLASS . These functions are marked const to indicate that they don’t modify the state of the object/variable for which they are called.
[3] A set of functions allowing the user to manipulate CLASS without actually having to know the details of the representation or fiddle with the intricacies of the semantics.
[4] A set of implicitly defined operations to allow CLASS to be freely copied.
[5] A class, B a d cls , to be used for reporting errors as exceptions.

Helper Functions
Typically, a class has a number of functions associated with it that need not be defined in the class itself because they don’t need direct access to the representation.

Defining such functions in the class itself would complicate the class interface and increase the number of functions that would potentially need to be examined when a change to the representation was considered.

Traditionally, their declarations were simply placed in the same file as the declaration of class, and users who needed this class would make them all available by including the file that defined the interface.

Overloaded Operators
It is often useful to add functions to enable conventional notation. For example, the o p e r a t o r == function defines the equality operator ==

The Significance of Concrete Classes
I call simple user defined types, such as D a t e , concrete types to distinguish them from abstract classes and class hierarchies and also to emphasize their similarity to built-in types such as i n t and c h a r . They have also been called value types, and their use value oriented programming. Their model of use and the ‘‘philosophy’’ behind their design are quite different from what is often advertised as object oriented programming.

The intent of a concrete type is to do a single, relatively small thing well and efficiently. It is not usually the aim to provide the user with facilities to modify the behavior of a concrete type. In particular, concrete types are not intended to display polymorphic behavior. If you don’t like some detail of a concrete type, you build a new one with the desired behavior. If you want to ‘‘reuse’’ a concrete type, you use it in the implementation of your new type exactly as you would have used an i n t .

Objects
Objects can be created in several ways. Some are local variables, some are global variables, some are members of classes, etc. The constructors used to initialize objects, and the destructors used to clean up objects before they become unusable.

Destructors
A constructor initializes an object. In other words, it creates the environment in which the member functions operate. Sometimes, creating that environment involves acquiring a resource – such as a file, a lock, or some memory – that must be released after use. Thus, some classes need a function that is guaranteed to be invoked when an object is destroyed in a manner similar to the way a constructor is guaranteed to be invoked when an object is created. Inevitably, such functions are called destructors. They typically clean up and release resources.

Destructors are called implicitly when an automatic variable goes out of scope, an object on the free store is deleted, etc. Only in very unusual circumstances does the user need to call a destructor explicitly.

Default Constructors
Most types can be considered to have a default constructor. A default constructor is a constructor that can be called without supplying an argument. If a user has declared a default constructor, that one will be used, otherwise, the compiler will try to generate one if needed and if the user hasn’t declared other constructors. A compiler generated default constructor implicitly calls the default constructors for a class’ members of class type and bases.

Because c o n s t s and references must be initialize, a class containing c o n s t or reference members cannot be default constructed unless the programmer explicitly supplies a constructor.

Default constructors can be invoked explicitly. Builtin types also have default constructors.

Construction and Destruction
Consider the different ways an object can be created and how it gets destroyed afterwards. An object can be created as:
1. A named automatic object, which is created each time its declaration is encountered in the execution of the program and destroyed each time the program exits the block in which it occurs
2. A freestore object, which is created using the n e w operator and destroyed using the d e l e t e operator
3. A nonstatic member object, which is created as a member of another class object and created and destroyed when the object of which it is a member is created and destroyed.
4. An array element, which is created and destroyed when the array of which it is an element is created and destroyed.
5. A local static object, which is created the first time its declaration is encountered in the execution of the program and destroyed once at the termination of the program.
6. A global, namespace, or class static object, which is created once ‘‘at the start of the program’’ and destroyed once at the termination of the program.
7. A temporary object, which is created as part of the evaluation of an expression and destroyed at the end of the full expression in which it occurs.
8. An object placed in memory obtained from a user supplied function guided by arguments supplied in the allocation operation.
9. A u n i o n member, which may not have a constructor or a destructor.

Local Variables
The constructor for a local variable is executed each time the thread of control passes through the declaration of the local variable. The destructor for a local variable is executed each time the local variable’s block is exited. Destructors for local variables are executed in reverse order of their construction.

Copying Objects
If t 1 and t 2 are objects of a class T a b l e, t 2 =t 1 by default means a memberwise copy of t 1 into t 2. Having assignment interpreted this way can cause a surprising (and usually undesired) effect when used on objects of a class with pointer members. Memberwise copy is usually the wrong semantics for copying objects containing resources managed by a constructor/destructor pair.

The programmer can define any suitable meaning for these copy operations, but the traditional one is to copy the contained elements (or at least to give the user of the appearance that a copy has been done)

As is almost always the case, the copy constructor and the copy assignment differ considerably. The fundamental reason is that a copy constructor initializes uninitialized memory, whereas the copy assignment operator must correctly deal with a well constructed object.

Assignment can be optimized in some cases, but the general strategy for an assignment operator is simple:
1. Protect against self assignment.
2. Delete old elements.
3. Initialize, and copy in new elements.
Usually every non-static member must be copied

Free Store
An object created on the free store has its constructor invoked by the new operator and exists until the delete operator is applied to a pointer to it.

Not deleting an object is typically not an error as far as the language is concerned; it is only a waste of space. However, in a program that is meant to run for a long time, such a memory leak is a serious and hard to find error. There are tools available for detecting such leaks. Deleting p twice is a serious error; the behavior is undefined and most likely disastrous.

Some C++ implementations automatically recycle the storage occupied by unreachable objects (garbage collecting implementations), but their behavior is not standardized. Even when a garbage collector is running, delete will invoke a destructor if one is defined, so it is still a serious error to delete an object twice. In many cases, that is only a minor inconvenience. In particular, where a garbage collector is known to exist, destructors that do memory management only can be eliminated. This simplification comes at the cost of portability and for some programs, a possible increase in run time and a loss of predictability of runtime behavior.

After delete has been applied to an object, it is an error to access that object in any way. Unfortunately, implementations cannot reliably detect such errors.

The user can specify how new does allocation and how delete does deallocation. It is also possible to specify the way an allocation, initialization (construction), and exceptions interact.

Class Objects as Members
The members’ constructors are called before the body of the containing class’ own constructor is executed. The constructors are called in the order in which they are declared in the class rather than the order in which they appear in the initializer list. To avoid confusion, it is best to specify the initializers in declaration order. The member destructors are called in the reverse order of construction.

If a member constructor needs no arguments, the member need not be mentioned in the member initializer list.

When a class object containing class objects is destroyed, the body of that object’s own destructor (if one is specified) is executed first and then the members’ destructors are executed in reverse order of declaration. A constructor assembles the execution environment for the member functions for a class from the bottom up (members first). The destructor disassembles it from the top down (members last).

Necessary Member Initialization
Member initializers are essential for types for which initialization differs from assignment – that is, for member objects of classes without default constructors, for const members, and for reference members.

There isn’t any other way to initialize such members, and it is an error not to initialize objects of those types. For most types, however, the programmer has a choice between using an initializer and using an assignment. In that case, I usually prefer to use the member initializer syntax, thus making explicit the fact that initialization is being done. Often, there also is an efficiency advantage to using the initializer syntax.

Member Constants
It is also possible to initialize a static integral constant member by adding a constant expression initializer to its member declaration.

Copying Members
A default copy constructor or default copy assignment copies all elements of a class. If this copy cannot be done, it is an error to try to copy an object of such a class.

In addition, a default assignment cannot be generated if a non-static member is a reference, a const, or a user defined type without a copy assignment.

Note that the default copy constructor leaves a reference member referring to the same object in both the original and the copied object. This can be a problem if the object referred to is supposed to be deleted.

When writing a copy constructor, we must take care to copy every element that needs to be copied. By default, elements are default initialized, but that is often not what is desired in a copy constructor.

Arrays
If an object of a class can be constructed without supplying an explicit initializer, then arrays of that class can be defined.

There is no way to specify explicit arguments for a constructor in an array declaration. If you absolutely must initialize members of an array with different values, you can write a default constructor that directly or indirectly reads and writes nonlocal data.

The destructor for each constructed element of an array is invoked when that array is destroyed. This is done implicitly for arrays that are not allocated using new. Like C, C++ doesn’t distinguish between a pointer to an individual object and a pointer to the initial element of an array. Consequently, the programmer must state whether an array or an individual object is being deleted.

Exactly how arrays and individual objects are allocated is implementation dependent. Therefore, different implementations will react differently to incorrect uses of the delete and delete[] operators. In simple and uninteresting cases like the previous one, a compiler can detect the problem, but generally something nasty will happen at run time.

The special destruction operator for arrays, delete[], isn’t logically necessary. However, suppose the implementation of the free store had been required to hold sufficient information for every
object to tell if it was an individual or an array. The user could have been relieved of a burden, but
that obligation would have imposed significant time and space overheads on some C++ implementations.

Local Static Store
The constructor for a local static object is called the first time the thread of control passes through the object’s definition.

The destructors for local static objects are invoked in the reverse order of their construction when the program terminates. Exactly when is unspecified.

Nonlocal Store
A variable defined outside any function (that is, global, namespace, and class static variables) is initialized (constructed) before main()is invoked, and any such variable that has been constructed will have its destructor invoked after exit from main(). Dynamic linking complicates this picture slightly by delaying the initialization until the code is linked into the running program. Constructors for nonlocal objects in a translation unit are executed in the order their definitions occur.

No implementation independent guarantees are made about the order of construction of nonlocal objects in different compilation units.

The order isn’t even guaranteed to be fixed in every particular implementation. Dynamic linking, or even a small change in the compilation process, can alter the sequence. The order of destruction is similarly implementation dependent.

Sometimes when you design a library, it is necessary, or simply convenient, to invent a type with a constructor and a destructor with the sole purpose of initialization and cleanup. Such a type would be used once only: to allocate a static object so that the constructor and the destructor are called.

Unfortunately, it is not guaranteed that such an object is initialized before its first use and destroyed after its last use in a program consisting of separately compiled units. A particular C++ implementation may provide such a guarantee, but most don’t. A programmer may ensure proper initialization by implementing the strategy that the implementations usually employ for local static objects: a first time switch.

If there are many functions that need to test the first time switch, this can be tedious, but it is often manageable. This technique relies on the fact that statically allocated objects without constructors are initialized to 0. The really difficult case is the one in which the first operation may be time critical so that the overhead of testing and possible initialization can be serious. In that case, further trickery is required

First time switches do not handle every conceivable situation. For example, it is possible to create objects that refer to each other during construction. Such examples are best avoided. If such objects are necessary, they must be constructed carefully in stages. Also, there is no similarly simple last time switch construct.

Temporary Objects
Temporary objects most often are the result of arithmetic expressions. For example, at some point in the evaluation of x*y+z the partial result x*y must exist somewhere. Except when performance is the issue, temporary objects rarely become the concern of the programmer. However, it happens. Unless bound to a reference or used to initialize a named object, a temporary object is destroyed at the end of the full expression in which it was created. A full expression is an expression that is not a sub expression of some other expression.

The standard string class has a member function cstr() that returns a Cstyle, zero terminated array of characters. Also, the operator + is defined to mean string concatenation. These are very useful facilities for strings. However, in combination they can cause obscure problems.

Please note that in this case, as in many others, the problems with temporaries arose from using a high level data type in a low level way. A cleaner programming style would have not only yielded a more understandable program fragment, but also avoided the problems with temporaries completely.

A temporary can be used as an initializer for a const reference or a named object. This is fine. The temporary is destroyed when ‘‘its’’ reference or named object go out of scope.

Remember that returning a reference to a local variable is an error and that a temporary object cannot be bound to a non const reference.

A temporary object can also be created by explicitly invoking a constructor. Such temporaries are destroyed in exactly the same way as the implicitly generated temporaries.

Placement of Objects
Placing an object in an area that is not (directly) controlled by the standard freestore manager implies that some care is required when destroying the object. The basic mechanism for that is an explicit call of a destructor

void destroy(X* p,Arena* a)
{
p>~
X() ; / / call destructor
a>
free(p) ; / / free memory
}

Note that explicit calls of destructors, like the use of special purpose global allocators, should be avoided wherever possible.

Unions
A named union is defined as a struct, where every member has the same address. A union can have member functions but not static members.

In general, a compiler cannot know what member of a union is used; that is, the type of the object stored in a union is unknown. Consequently, a union may not have members with constructors or destructors. It wouldn’t be possible to protect that object against corruption or to guarantee that the right destructor is called when the union goes out of scope.

Unions are best used in low level code, or as part of the implementation of classes that keep track of what is stored in the union.

Advice
[1] Represent concepts as classes.
[2] Use public data (structs) only when it really is just data and no invariant is meaningful for the data members.
[3] A concrete type is the simplest kind of class. Where applicable, prefer a concrete type over more complicated classes and over plain data structures.
[4] Make a function a member only if it needs direct access to the representation of a class.
[5] Use a namespace to make the association between a class and its helper functions explicit.
[6] Make a member function that doesn’t modify the value of its object a const member function.
[7] Make a function that needs access to the representation of a class but needn’t be called for a specific object a static member function.
[8] Use a constructor to establish an invariant for a class.
[9] If a constructor acquires a resource, its class needs a destructor to release the resource.
[10] If a class has a pointer member, it needs copy operations (copy constructor and copy assignment.
[11] If a class has a reference member, it probably needs copy operations (copy constructor and copy assignment.
[12] If a class needs a copy operation or a destructor, it probably needs a constructor, a destructor, a copy assignment, and a copy constructor.
[13] Check for self assignment in copy assignments.
[14] When writing a copy constructor, be careful to copy every element that needs to be copied (beware of default initializers.
[15] When adding a new member to a class, always check to see if there are user defined constructors that need to be updated to initialize the member.
[16] Use enumerators when you need to define integer constants in class declarations.
[17] Avoid order dependencies when constructing global and namespace objects.
[18] Use first time switches to minimize order dependencies.
[19] Remember that temporary objects are destroyed at the end of the full expression in which they are created.

No comments:

Post a Comment