Google
 

Wednesday, February 9, 2011

C++ Primer, Fourth Edition:Notes Chapter 12. Classes

Section 12.1 Class Definitions and Declarations

In C++ we use classes to define our own abstract data types. By defining types that mirror concepts in the problems we are trying to solve, we can make our programs easier to write, debug, and modify

Classes are the most important feature in C++. Early versions of the language were named "C with Classes," emphasizing the central role of the class facility. As the language evolved, support for building classes increased. A primary goal of the language design has been to provide features that allow programmers to define their own types that are as easy and intuitive to use as the built-in types. This chapter presents many of the basic features of classes

 

  • 12.1.1. Class Definitions: A Recap
    • Most fundamentally, a class defines a new type and a new scope
    • Class Members
      • Each class defines zero or more members. Members can be either data, functions, or type definitions
      • A class may contain multiple public, private, and protected sections
      • All members must be declared inside the class; there is no way to add members once the class definition is complete
    • Constructors
      • When we create an object of a class type, the compiler automatically uses a constructor (Section 2.3.3, p. 49) to initialize the object. A constructor is a special member function that has the same name as the class. Its purpose is to ensure that each data member is set to sensible initial values
      • A constructor generally should use a constructor initializer list (Section 7.7.3, p. 263), to initialize the data members of the object
      • The constructor initializer list is a list of member names and parenthesized initial values. It follows the constructor's parameter list and begins with a colon
    • Member Functions
      • Member functions must be declared, and optionally may be defined, inside the class; functions defined inside the class are inline (Section 7.6, p. 256) by default
      • Member functions defined outside the class must indicate that they are in the scope of the class. The definition of Sales_item::avg_price uses the scope operator (Section 1.2.2, p. 8) to indicate that the definition is for the avg_price function of the Sales_item class
      • Member functions take an extra implicit argument that binds the function to the object on behalf of which the function is calledwhen we write trans.avg_price() we are calling the avg_price function on the object named trans. If TRans is a Sales_item object, then references to a member of the Sales_item class inside the avg_price function are to the members in trans.
      • A const member may not change the data members of the object on which it operates. The const must appear in both the declaration and definition. It is a compile-time error for the const to be indicated on one but not the other
  • 12.1.2. Data Abstraction and Encapsulation
    • The fundamental ideas behind classes are data abstraction and encapsulation
    • Data abstraction is a programming (and design) technique that relies on the separation of interface and implementation. The class designer must worry about how a class is implemented, but programmers that use the class need not know about these details. Instead, programmers who use a type need to know only the type's interface; they can think abstractly about what the type does rather than concretely about how the type works
    • Encapsulation is a term that describes the technique of combining lower-level elements to form a new, higher-level entity. A function is one form of encapsulation: The detailed actions performed by the function are encapsulated in the larger entity that is the function itself. Encapsulated elements hide the details of their implementationwe may call a function but have no access to the statements that it executes. In the same way, a class is an encapsulated entity: It represents an aggregation of several members, and most (well-designed) class types hide the members that implement the type
    • Access Labels Enforce Abstraction and Encapsulation
      • Members defined after a public label are accessible to all parts of the program. The data-abstraction view of a type is defined by its public members
      • Members defined after a private label are not accessible to code that uses the class. The private sections encapsulate (e.g., hide) the implementation from code that uses the type
      • There are no restrictions on how often an access label may appear. Each access label specifies the access level of the succeeding member definitions. The specified access level remains in effect until the next access label is encountered or the closing right brace of the class body is seen
      • A class may define members before any access label is seen. The access level of members defined after the open curly of the class and before the first access label depend on how the class is defined. If the class is defined with the struct keyword, then members defined before the first access label are public; if the class is defined using the class keyword, then the members are private

    Advice: Concrete and Abstract Types

    Not all types need to be abstract. The library pair class is a good example of a useful, well-designed class that is concrete rather than abstract. A concrete class is a class that exposes, rather than hides, its implementation.

    Some classes, such as pair, really have no abstract interface. The pair type exists to bundle two data members into a single object. There is no need or advantage to hiding the data members. Hiding the members in a class like pair would only complicate the use of the type.

    Even so, such types often have member functions. In particular, it is a good idea for any class that has data members of built-in or compound type to define constructor(s) to initialize those members. The user of the class could initialize or assign to the data members but it is less error-prone for the class to do so.


      • Different Kinds of Programming Roles
        • When designing the interface to a class, the class designer should think about how easy it will be to use the class. When using the class, the designer shouldn't think about how the class works
        • C++ programmers tend to speak of "users" interchangably as users of the application or users of a class
        • Key Concept: Benefits of Data Abstraction and Encapsulation

          Data abstraction and encapsulation provide two important advantages:

          • Class internals are protected from inadvertent user-level errors, which might corrupt the state of the object.

          • The class implementation may evolve over time in response to changing requirements or bug reports without requiring change in user-level code.

          By defining data members only in the private section of the class, the class author is free to make changes in the data. If the implementation changes, only the class code needs to be examined to see what affect the change may have. If data are public, then any function that directly accesses the data members of the old representation might be broken. It would be necessary to locate and rewrite all those portions of code that relied on the old representation before the program could be used again.

          Similarly, if the internal state of the class is private, then changes to the member data can happen in only a limited number of places. The data is protected from mistakes that users might introduce. If there is a bug that corrupts the object's state, the places to look for the bug are localized: When data are private, only a member function could be responsible for the error. The search for the mistake is limited, greatly easing the problems of maintenance and program correctness.

          If the data are private and if the interface to the member functions does not change, then user functions that manipulate class objects require no change.

          Because changing a class definition in a header file effectively changes the text of every source file that includes that header, code that uses a class must be recompiled when the class changes.




    • 12.1.3. More on Class Definitions
      • Multiple Data Members of the Same Type
        • One way in which member declarations and ordinary declarations are the same is that if a class has multiple data members with the same type, these members can be named in a single member declaration
      • Using Typedefs to Streamline Classes
        • In addition to defining data and function members, a class can also define its own local names for types
        • Type names defined by a class obey the standard access controls of any other member. We put the definition of index in the public part of the class because we want users to use that name
      • Member Functions May Be Overloaded
        • With the exception of overloaded operators (Section 14.9.5, p. 547)which have special rulesa member function overloads only other member functions of its own class. A class member function is unrelated to, and cannot overload, ordinary nonmember functions or functions declared in other classes. The same rules apply to overloaded member functions as apply to plain functions: Two overloaded members cannot have the same number and types of parameters
      • Defining Overloaded Member Functions
      • Explicitly Specifying inline Member Functions
        • We can specify that a member is inline as part of its declaration inside the class body. Alternatively, we can specify inline on the function definition that appears outside the class body. It is legal to specify inline both on the declaration and definition. One advantage of defining inline functions outside the class is that it can make the class easier to read
        • As with other inlines, the definition of an inline member function must be visible in every source file that calls the function. The definition for an inline member function that is not defined within the class body ordinarily should be placed in the same header file in which the class definition appears
    • 12.1.4. Class Declarations versus Definitions
      • A class is completely defined once the closing curly brace appears. Once the class is defined, all the class members are known
      • The size required to store an object of the class is known as well
      • A class may be defined only once in a given source file. When a class is defined in multiple files, the definition in each file must be identical.
      • By putting class definitions in header files, we can ensure that a class is defined the same way in each file that uses it. By using header guards (Section 2.9.2, p. 69), we ensure that even if the header is included more than once in the same file, the class definition will be seen only once
      • It is possible to declare a class without defining it
      • This declaration, sometimes referred to as a forward declaration, introduces the name Screen into the program and indicates that Screen refers to a class type. After a declaration and before a definition is seen, the type Screen is an incompete typeit's known that Screen is a type but not known what members that type contains
      • An incomplete type can be used only in limited ways. Objects of the type may not be defined. An incomplete type may be used to define only pointers or references to the type or to declare (but not define) functions that use the type as a paremeter or return type
      • A data member can be specified to be of a class type only if the definition for the class has already been seen. If the type is incomplete, a data member can be only a pointer or a reference to that class type
      • However, a class is considered declared as soon as its class name has been seen. Therefore, a class can have data members that are pointers or references to its own type
      • A common use of class forward declarations is to write classes that are mutually dependent on one another
    • 12.1.5. Class Objects
      • When we define a class, we are defining a type. Once a class is defined, we can define objects of that type. Storage is allocated when we define objects, but (ordinarily) not when we define types
      • Each object has its own copy of the class data members. Modifying the data members of item does not change the data members of any other Sales_item object
      • Defining Objects of Class Type
        • Using the class name directly as a type name
      • Why a Class Definition Ends in a Semicolon
        • A semicolon is required because we can follow a class definition by a list of object definitions. As always, a definition must end in a semicolon
        • Ordinarily, it is a bad idea to define an object as part of a class definition. Doing so obscures what's happening. It is confusing to readers to combine definitions of two different entitiesthe class and a variablein a single statement

    Section 12.2 The Implicit this Pointer

    • member functions have an extra implicit parameter that is a pointer to an object of the class type. This implicit parameter is named this, and is bound to the object on which the member function is called
    • Member functions may not define the this parameter; the compiler does so implicitly. The body of a member function may explicitly use the this pointer, but is not required to do so. The compiler treats an unqualified reference to a class member as if it had been made through the this pointer
    • When to Use the this Pointer
      • there is one case in which we must do so: when we need to refer to the object as a whole rather than to a member of the object
    • Returning *this
    • Returning *this from a const Member Function
      • In an ordinary nonconst member function, the type of this is a const pointer (Section 4.2.5, p. 126) to the class type. We may change the value to which this points but cannot change the address that this holds
      • In a const member function, the type of this is a const pointer to a const class-type object. We may change neither the object to which this points nor the address that this holds
      • We cannot return a plain reference to the class object from a const member function. A const member function may return *this only as a const reference
    • Overloading Based on const
      • We can overload a member function based on whether it is const for the same reasons that we can overload a function based on whether a pointer parameter points to const
      • A const object will use only the const member. A nonconst object could use either member, but the nonconst version is a better match
    • Mutable Data Members
      • It sometimes (but not very often) happens that a class has a data member that we want to be able to modify, even inside a const member function. We can indicate such members by declaring them as mutable
      • A mutable data member is a member that is never const, even when it is a member of a const object. Accordingly, a const member function may change a mutable member

        Advice: Use Private Utility Functions for Common Code

        Some readers might be surprised that we bothered to define a separate do_display operation. After all, the calls to do_display aren't much simpler than the action done inside do_display. Why bother? We do so for several reasons:

        1. A general desire to avoid writing the same code in more than one place.

        2. The display operation can be expected to become more complicated as our class evolves. As the actions involved become more complex, it makes more obvious sense to write those actions in one place, not two.

        3. It is likely that we might want to add debugging information to do_display during development that would be eliminated in the final product version of the code. It will be easier to do so if only one definition of do_display needs to be changed to add or remove the debugging code.

        4. There needn't be any overhead involved in this extra function call. We made do_display inline, so the run-time performance between calling do_display or putting the code directly into the display operations should be identical.

        In practice, well-designed C++ programs tend to have lots of small functions such as do_display that are called to do the "real" work of some other set of functions.


      • a

    Section 12.3 Class Scope

    1. Every class defines its own new scope and a unique type. The declarations of the class members within the class body introduce the member names into the scope of their class. Two different classes have two different class scopes
    2. Even if two classes have exactly the same member list, they are different types. The members of each class are distinct from the members of any other class (or any other scope).
    3. Using a Class Member
      1. Outside the class scope, members may be accessed only through an object or a pointer using member access operators dot or arrow, respectively
      2. Some members are accessed using the member access operators; others are accessed directly from the class using the scope operator, (::). Ordinary data or function members must be accessed through an object. Members that define types, such as Screen::index, are accessed using the scope operator
    4. Scope and Member Definitions
      1. Member definitions behave as if they are in the scope of the class, even if the member is defined outside the class body
    5. Parameter Lists and Function Bodies Are in Class Scope
      1. These are defined inside the class scope and so may refer to other class members without qualification
    6. Function Return Types Aren't Always in Class Scope
      1. If the function is defined outside the class body, then the name used for the return type is outside the class scope. If the return type uses a type defined by the class, it must use the fully qualified name
    7. 12.3.1. Name Lookup in Class Scope
      1. In the programs we've written so far, name lookup (the process of finding which declaration is matched to a given use of a name) has been relatively straightforward
        1. First, look for a declaration of the name in the block in which the name was used. Only names declared before the use are considered.
        2. If the name isn't found, the enclosing scope(s) are searched.
      2. If no declaration is found, then the program is in error. In C++ programs, all names must be declared before they are used
      3. Class scopes may seem to behave a bit differently, but in reality they obey this same rule. Confusion can arise due to the way names are resolved inside a function defined within the class body itself
      4. Class definitions are actually processed in two phases
        1. First, the member declarations are compiled
        2. Only after all the class members have been seen are the definitions themselves compiled
      5. Of course, the names used in class scope do not always have to be class member names. Name lookup in class scope finds names declared in other scopes as well. During name lookup, if a name used in class scope does not resolve to a class member name, the scopes surrounding the class or member definition are searched to find a declaration for the name
      6. Name Lookup for Class Member Declarations
        1. Names used in the declarations of a class member are resolved as follows
          1. The declarations of the class members that appear before the use of the name are considered
          2. If the lookup in step 1 is not successful, the declarations that appear in the scope in which the class is defined, and that appear before the class definition itself, are considered
        2. Names of types defined in a class must be seen before they are used as the type of a data member or as the return type or parameter type(s) of a member function
        3. The compiler handles member declarations in the order in which they appear in the class. As usual, a name must be defined before it can be used. Moreover, once a name has been used as the name of a type, that name may not be redefined
      7. Name Lookup in Class Member Definitions
        1. A name used in the body of a member function is resolved as follows
          1. Declarations in the member-function local scopes are considered first
          2. If the a declaration for the name is not found in the member function, the declarations for all the class members are considered
          3. If a declaration for the name is not found in the class, the declarations that appear in scope before the member function definition are considered
      8. Class Members Follow Normal Block-Scope Name Lookup
        1. Programs that illustrate how name lookup works often have to rely on bad practices. The next several programs contain bad style deliberately
        2. The following function uses the same name for a parameter and a member, which normally should be avoided
        3. Even though the class member is hidden, it is still possible to use it by qualifying the member's name with the name of its class or by using the this pointer explicitly
      9. After Function Scope, Look in Class Scope
      10. After Class Scope, Look in the Surrounding Scope
        1. Even though the global object is hidden, it is still possible to use it by qualifying the name with the global scope resolution operator
      11. Names Are Resolved Where They Appear within the File
        1. When a member is defined outside the class definition, the third step of name lookup not only considers the declarations in global scope that appear before the definition of class Screen, but also considers the global scope declarations that appear before the member function definition

    Section 12.4 Constructors

    1. Constructors (Section 2.3.3, p. 49) are special member functions that are executed whenever we create new objects of a class type. The job of a constructor is to ensure that the data members of each object start out with sensible initial values
    2. Constructors have the same name as the name of the class and may not specify a return type. Like any other function, they may define zero or more parameters
    3. Constructors May Be Overloaded
      1. There is no constraint on the number of constructors we may declare for a class, provided that the parameter list of each constructor is unique. How can we know which or how many constructors to define? Ordinarily, constructors differ in ways that allow the user to specify differing ways to initialize the data members
    4. Arguments Determine Which Constructor to Use
      1. The argument type(s) used to initialize an object determines which constructor is used
    5. Constructors Are Executed Automatically
      1. The compiler runs a constructor whenever an object of the type is created
    6. Constructors for const Objects
      1. A constructor may not be declared as const
      2. There is no need for a const constructor. When we create a const object of a class type, an ordinary constructor is run to initialize the const object. The job of the constructor is to initialize an object. A constructor is used to initialize an object regardless of whether the object is const
    7. 12.4.1. The Constructor Initializer
      1. Like any other function, a constructor has a name, a parameter list, and a function body. Unlike other functions, a constructor may also contain a constructor initializer list
      2. The constructor initializer starts with a colon, which is followed by a comma-separated list of data members each of which is followed by an initializer inside parentheses
      3. As with any member function, constructors can be defined inside or outside of the class. The constructor initializer is specified only on the constructor definition, not its declaration
      4. The constructor initializer is a feature that many reasonably experienced C++ programmers have not mastered
      5. Data members of class type are always initialized in the initialization phase, regardless of whether the member is initialized explicitly in the constructor initializer list. Initialization happens before the computation phase begins
      6. Each member that is not explicitly mentioned in the constructor initializer is initialized using the same rules as those used to initialize variables (Section 2.3.4, p. 50). Data members of class type are initialized by running the type's default constructor. The initial value of members of built-in or compound type depend on the scope of the object: At local scope those members are uninitialized, at global scope they are initialized to 0
      7. Constructor Initializers Are Sometimes Required
        1. If an initializer is not provided for a class member, then the compiler implicitly uses the default constructor for the member's type. If that class does not have a default constructor, then the attempt by the compiler to use it will fail. In such cases, an initializer must be provided in order to initialize the data member
        2. Some members must be initialized in the constructor initializer. For such members, assigning to them in the constructor body doesn't work. Members of a class type that do not have a default constructor and members that are const or reference types must be initialized in the constructor initializer regardless of type
        3. Remember that we can initialize but not assign to const objects or objects of reference type. By the time the body of the constructor begins executing, initialization is complete. Our only chance to initialize const or reference data members is in the constructor initializer

          Advice: Use Constructor Initializers

          In many classes, the distinction between initialization and assignment is strictly a matter of low-level efficiency: A data member is initialized and assigned when it could have been initialized directly. More important than the efficiency issue is the fact that some data members must be initialized.

          We must use an initializer for any const or reference member or for any member of a class type that does not have a default constructor.



          By routinely using constructor initializers, we can avoid being surprised by compile-time errors when we have a class with a member that requires a constructor initializer.

      8. Order of Member Initialization
        1. Not surprisingly, each member may be named only once in the constructor initializer. After all, what might it mean to give a member two initial values? What may be more surprising is that the constructor initializer list specifies only the values used to initialize the members, not the order in which those initializations are performed. The order in which members are initialized is the order in which the members are defined. The first member is initialized first, then the next, and so on
        2. The order of initialization often doesn't matter. However, if one member is initialized in terms of another, then the order in which members are initialized is crucially important
        3. It is a good idea to write constructor initializers in the same order as the members are declared. Moreover, when possible, avoid using members to initialize other members
        4. It is often the case that we can avoid any problems due to order of execution for initializers by (re)using the constructor's parameters rather than using the object's data members
      9. Initializers May Be Any Expression
        1. An initializer may be an arbitrarily complex expression
      10. Initializers for Data Members of Class Type
        1. When we initialize a member of class type, we are specifying arguments to be passed to one of the constructors of that member's type. We can use any of that type's constructors
    8. 12.4.2. Default Arguments and Constructors
      1. We can combine these constructors by supplying a default argument for the string initializer
      2. We prefer to use a default argument because it reduces code duplication.
      3. Smile 
        class Sales_item {
             public:
                 // default argument for book is the empty string
                 Sales_item(const std::string &book = ""):
                           isbn(book), units_sold(0), revenue(0.0) { }
                 Sales_item(std::istream &is);
                 // as before
             };
         
         

    9. 12.4.3. The Default Constructor

      1. The default constructor is used whenever we define an object but do not supply an initializer. A constructor that supplies default arguments for all its parameters also defines the default constructor
      2. The Synthesized Default Constructor

        1. If a class defines even one constructor, then the compiler will not generate the default constructor. The basis for this rule is that if a class requires control to initialize an object in one case, then the class is likely to require control in all cases
        2. The compiler generates a default constructor automatically only if a class defines no constructors
        3. The synthesized default constructor initializes members using the same rules as those that apply for how variables are initialized. Members that are of class type are initialized by running each member's own default constructor. Members of built-in or compound type, such as pointers and arrays, are initialized only for objects that are defined at global scope. When objects are defined at local scope, then members of built-in or compound type are uninitialized
        4. If a class contains data members of built-in or compound type, then the class should not rely on the synthesized default constructor. It should define its own constructor to initialize these members

      3. Classes Should Usually Define a Default Constructor

        1. In certain cases, the default constructor is applied implicitly by the compiler. If the class has no default constructor, then the class may not be used in these contexts
        2. In practice, it is almost always right to provide a default constructor if other constructors are being defined. Ordinarily the initial values given to the members in the default constructor should indicate that the object is "empty."

      4. Using the Default Constructor

    10. 12.4.4. Implicit Class-Type Conversions

      1. To define an implicit conversion to a class type, we need to define an appropriate constructor.
      2. Suppressing Implicit Conversions Defined by Constructors

        1. We can prevent the use of a constructor in a context that requries an implicit conversion by declaring the constructor explicit
        2. The explicit keyword is used only on the constructor declaration inside the class. It is not repeated on a definition made outside the class body
        3. When a constructor is declared explicit, the compiler will not use it as a conversion operator

      3. Explicitly Using Constructors for Conversions

        1. An explicit constructor can be used to generate a conversion as long as we do so explicitly
        2. Making a constructor explicit turns off only the use of the constructor implicitly. Any constructor can be used to explicitly create a temporary object
        3. Ordinarily, single-parameter constructors should be explicit unless there is an obvious reason to want to define an implicit conversion. Making constructors explicit may avoid mistakes, and a user can explicitly construct an object when a conversion is useful

    11. 12.4.5. Explicit Initialization of Class Members

      1. Although most objects are initialized by running an appropriate constructor, it is possible to initialize the data members of simple nonabstract classes directly. Members of classes that define no constructors and all of whose data members are public may be initialized in the same way that we initialize array elements

        struct Data {
            int ival;
            char *ptr;
        };
        // val1.ival = 0; val1.ptr = 0
        Data val1 = { 0, 0 };
         
        // val2.ival = 1024;
        // val2.ptr = "Anna Livia Plurabelle"
        Data val2 = { 1024, "Anna Livia Plurabelle" };

      2. This form of initialization is inherited from C and is supported for compatibility with C programs. There are three significant drawbacks to explicitly initializing the members of an object of class type

        1. It requires that all the data members of the class be public
        2. It puts the burden on the programmer to initialize every member of every object. Such initialization is tedious and error-prone because it is easy to forget an initializer or to supply an inappropriate initializer
        3. If a member is added or removed, all initializations have to be found and updated correctly

      3. It is almost always better to define and use constructors. When we provide a default constructor for the types we define, we allow the compiler to automatically run that constructor, ensuring that every class object is properly initialized prior to the first use of that object


    Section 12.5 Friends



    1. In some cases, it is convenient to let specific nonmember functions access the private members of a class while still preventing general access

    2. The friend mechanism allows a class to grant access to its nonpublic members to specified functions or classes. A friend declaration begins with the keyword friend. It may appear only within a class definition. Friend declarations may appear anywhere in the class: Friends are not members of the class granting friendship, and so they are not affected by the access control of the section in which they are declared

    3. Ordinarily it is a good idea to group friend declarations together either at the beginning or end of the class definition

    4. Friendship: An Example


      1. A friend may be an ordinary, nonmember function, a member function of another previously defined class, or an entire class. In making a class a friend, all the member functions of the friend class are given access to the nonpublic members of the class granting friendship

    5. Making Another Class' Member Function a Friend


      1. When we declare a member function to be a friend, the name of the function must be qualified by the name of the class of which it is a member

    6. Friend Declarations and Scope


      1. Interdependencies among friend declarations and the definitions of the friends can require some care in order to structure the classes correctly

      2. More generally, to make a member function a friend, the class containing that member must have been defined. On the other hand, a class or nonmember function need not have been declared to be made a friend

      3. A friend declaration introduces the named class or nonmember function into the surrounding scope. Moreover, a friend function may be defined inside the class. The scope of the function is exported to the scope enclosing the class definition

    7. Overloaded Functions and Friendship


      1. A class must declare as a friend each function in a set of overloaded functions that it wishes to make a friend



    Section 12.6 static Class Members



    1. It is sometimes necessary for all the objects of a particular class type to access a global object. Perhaps a count is needed of how many objects of a particular class type have been created at any one point in the program, or the global object may be a pointer to an error-handling routine for the class, or it may be a pointer to the free-store memory for objects of this class type

    2. Ordinary, nonstatic data members exist in each object of the class type. Unlike ordinary data members, a static data member exists independently of any object of its class; each static data member is an object associated with the class, not with the objects of that class

    3. Just as a class may define shared static data members, it may also define static member functions. A static member function has no this parameter. It may directly access the static members of its class but may not directly use the nonstatic members

    4. Advantages of Using Class static Members


      1. The name of a static member is in the scope of the class, thereby avoiding name collisions with members of other classes or global objects

      2. Encapsulation can be enforced. A static member can be a private member; a global object cannot

      3. It is easy to see by reading the program that a static member is associated with a particular class. This visibility clarifies the programmer's intentions

    5. Defining static Members


      1. Amember ismade static by prefixing the member declaration with the keyword static. The static members obey the normal public/private access rules

    6. Using a Class static Member


      1. A static member can be invoked directly from the class using the scope operator or indirectly through an object, reference, or pointer to an object of its class type

      2. As with other members, a class member function can refer to a class static member without the use of the scope operator


    7. 12.6.1. static Member Functions


      1. When we define a static member outside the class, we do not respecify the static keyword.

      2. static Functions Have No this Pointer


        1. A static member is part of its class but not part of any object. Hence, a static member function does not have a this pointer. Referring to this either explicitly or implicitly by using a nonstatic member is a compile-time error

        2. Because a static member is not part of any object, static member functions may not be declared as const. After all, declaring a member function as const is a promise not to modify the object of which the function is a member. Finally, static member functions may also not be declared as virtual


    8. 12.6.2. static Data Members


      1. static data members can be declared to be of any type. They can be consts, references, arrays, class types, and so forth

      2. static data members must be defined (exactly once) outside the class body. Unlike ordinary data members, static members are not initialized through the class constructor(s) and instead should be initialized when they are defined

      3. The best way to ensure that the object is defined exactly once is to put the definition of static data members in the same file that contains the definitions of the class noninline member functions

      4. static data members are defined in the same way that other class members and other variables are defined. The member is defined by naming its type followed by the fully qualified name of the member

      5. As with any class member, when we refer to a class static member outside the class body, we must specify the class in which the member is defined. The static keyword, however, is used only on the declaration inside the class body. Definitions are not labeled static

      6. Integral const static Members Are Special


        1. Ordinarily, class static members, like ordinary data members, cannot be initialized in the class body. Instead, static data members are normally initialized when they are defined.

        2. When a const static data member is initialized in the class body, the data member must still be defined outside the class definition

        3. When an initializer is provided inside the class, the definition of the member must not specify an initial value

      7. static Members Are Not Part of Class Objects


        1. Ordinary members are part of each object of the given class. static members exist independently of any object and are not part of objects of the class type. Because static data members are not part of any object, they can be used in ways that would be illegal for nonstatic data members

        2. the type of a static data member can be the class type of which it is a member. A nonstatic data member is restricted to being declared as a pointer or a reference to an object of its class

        3. Similarly, a static data member can be used as a default argument

        4. A nonstatic data member may not be used as a default argument because its value cannot be used independently of the object of which it is a part. Using a nonstatic data member as a default argument provides no object from which to obtain the member's value and so is an error


    No comments: