Search     or:     and:
 LINUX 
 Language 
 Kernel 
 Package 
 Book 
 Test 
 OS 
 Forum 
 iakovlev.org 
 Books
  Краткое описание
 Linux
 W. R. Стивенс TCP 
 W. R. Стивенс IPC 
 A.Rubini-J.Corbet 
 K. Bauer 
 Gary V. Vaughan 
 Д Вилер 
 В. Сталлинг 
 Pramode C.E. 
 Steve Pate 
 William Gropp 
 K.A.Robbins 
 С Бекман 
 Р Стивенс 
 Ethereal 
 Cluster 
 Languages
 C
 Perl
 M.Pilgrim 
 А.Фролов 
 Mendel Cooper 
 М Перри 
 Kernel
 C.S. Rodriguez 
 Robert Love 
 Daniel Bovet 
 Д Джеф 
 Максвелл 
 G. Kroah-Hartman 
 B. Hansen 
NEWS
Последние статьи :
  Тренажёр 16.01   
  Эльбрус 05.12   
  Алгоритмы 12.04   
  Rust 07.11   
  Go 25.12   
  EXT4 10.11   
  FS benchmark 15.09   
  Сетунь 23.07   
  Trees 25.06   
  Apache 03.02   
 
TOP 20
 MINIX...3057 
 Solaris...2933 
 LD...2906 
 Linux Kernel 2.6...2487 
 William Gropp...2182 
 Rodriguez 6...2017 
 C++ Templates 3...1946 
 Trees...1939 
 Kamran Husain...1866 
 Secure Programming for Li...1792 
 Максвелл 5...1711 
 DevFS...1695 
 Part 3...1684 
 Stein-MacEachern-> Час...1632 
 Go Web ...1627 
 Ethreal 4...1619 
 Стивенс 9...1607 
 Arrays...1607 
 Максвелл 1...1594 
 FAQ...1539 
 
  01.01.2024 : 3621733 посещений 

iakovlev.org

О чем этот раздел?

Мы рассмотрим ситуацию,когда класс включает в себя статические члены-данные.

    Что такое статические члены класса?

    В следующем примере, класс Fred имеет статический член данных x_ и нормальный член данных y_. Существует только одна копия Fred::x_ независимо от того, сколько обьектов Fred будет создано , и столько экземпляров y_ , сколько обьектов Fred будет создано. Аналогично для статической функции f() и обычной g().

     class Fred {
     public:
       static void f() throw();                           <-- 1
       void g() throw();                                  <-- 2
     protected:
       static int x_;                                     <-- 3
       int y_;                                            <-- 4
     };
     

    (1) Member function associated with the class

    (2) Member function associated with an individual object of the class

    (3) Data member associated with the class

    (4) Data member associated with an individual object of the class

    Everything except instance data members must be defined somewhere, such as in the Fred.cpp source file:

     #include "Fred.hpp"
     
     void Fred::f() throw()                               <-- 1
     { /*...*/ }
     
     void Fred::g() throw()
     { /*...*/ }
     
     int Fred::x_ = 3;                                    <-- 2
     

    (1) The static keyword is not used at the function's definition

    (2) Static data members must be explicitly defined in exactly one source file

    Static data members are often referred to as class data, and static member functions are often referred to as class services or class methods.

      На что похожи статические члены данных?

      Static data members are like data located in the factory rather than in the objects produced by the factory.

      In Detroit, there's a big sign with a running total of the number of cars produced during the current year. But that information isn't under the hood of any given car; all the car knows is a serial number indicating its ordinal number. The total number of cars produced is therefore factory data.

      In the following example, class Car is the factory that is used to produce Car objects. Every car has a serial number (serial_). The factory keeps count of the number of cars that have been built via num_, which is a class (or static) datum; serial_ is an object (or instance) datum. The constructors of class Car are responsible for incrementing the number of cars that have been built; for simplicity this number is used as the serial number.

       #include <iostream>
       using namespace std;
       class Car {
       public:
       
         Car() throw();                                     <-- 1
         Car(const Car& c) throw();                         <-- 2
       
         // No need for an explicit assignment operator or destructor
         // since these don't create new Car objects (but see FAQ 30.06).
       
         static int num_;                                   <-- 3
       
       private:
         int serial_;                                       <-- 4
       };
       
       Car::Car() throw()
       : serial_(num_)
       {
         cout << "Car ctor\n";
         ++num_;
       }
       
       Car::Car(const Car& c) throw()
       : serial_(num_)
       {
         cout << "Car copy\n";
         ++num_;
       }
       
       int Car::num_ = 0;                                   <-- 5
       

      (1) Increments Car::num_

      (2) Increments Car::num_

      (3) Class data

      (4) Object data

      (5) Class data is automatically initialized, often before main()

      Just as a factory exists before it produces its first object, class (static) data can be accessed before the first object is instantiated as well as after the last object has been destroyed.

       int main()
       {
         cout << "Car::num_ = " << Car::num_ << '\n';
         {
           Car a;
           cout << "Car::num_ = " << Car::num_ << '\n';
           Car b;
           cout << "Car::num_ = " << Car::num_ << '\n';
           Car c = a;
           cout << "Car::num_ = " << Car::num_ << '\n';
         }
         cout << "Car::num_ = " << Car::num_ << '\n';
       }
       

      The output is

       Car::num_ = 0
       Car ctor
       Car::num_ = 1
       Car ctor
       Car::num_ = 2
       Car copy
       Car::num_ = 3
       Car::num_ = 3
       

      Note: See the next FAQ regarding inline functions that access static data members.

        Можно ли из inline-функции получить надежный доступ к статическим членам данных?

        No!

        Static data members should normally be accessed by non-inline functions only (and then only from non-inline functions that are defined in the same source file as the static data member's definition). In some cases inline functions can access static member data, but the programmer needs to think through the issues fairly carefullythey're somewhat tricky.

        Suppose class Fred contains two static data members: Fred::i_ is of type int and Fred::s_ is of class string, the standard string class. The data member Fred::i_ is initialized before any code starts running, so Fred::i_ can be accessed from an inline function. However if an inline function accesses Fred::s_, and if the inline function is called from another compilation unit, the inline function might access Fred::s_ before it has been initialized (that is, before the constructor of Fred::s_ has run). This would be a disaster.

        Static data members are guaranteed to be initialized before the first call to any non-inline function within the same source file as the static data's definition. In the following example, file Fred.cpp defines both static data member Fred::s_ and member function Fred::f(). This means that Fred::s_ will be initialized before Fred::f() is called. But if someone calls inline function Fred::g() before calling Fred::f(), accessing Fred::s_ could be a disaster since Fred::s_ might not be initialized yet.

        Here is file Fred.hpp.

         #include <string>
         #include <iostream>
         using namespace std;
         
         class Fred {
         public:
           static void f() throw();
           static string g() throw();
         private:
           static string s_;
         };
         
         inline string Fred::g() throw()
         { cout << s_; return s_; }                           <-- 1
         

        (1) EVIL: Fred::s_ might not be initialized yet

        Here is file Fred.cpp.

         #include "Fred.hpp"
         
         string Fred::s_ = "Hello";
         
         void Fred::f() throw()
         { cout << s_; }                                      <-- 1
         

        (1) GOOD: Fred::s_ is guaranteed to be initialized

        Here is an example showing how the above code could possibly fail (this code is assumed to be in a different source file, such as main.cpp).

         #include "Fred.hpp"
         
         int main()
         { Fred::g(); }
         

        Note that somebut not allcompilers will initialize static data member Fred::s_ before main() begins. Thus this code is doubly evil since it will subtly fail on some compilers and accidentally work on others. In fact, its success or failure might even depend on the order that object files are passed to the linker, and some visual environments hide the linker so well that many programmers don't even know the order in which object files are passed to the linker.

        To make matters worse, the following source file, say even-worse.cpp, calls inline function Fred::g()therefore accessing Fred::s_during static initialization. Many compilers will cause this to happen before main() begins, so this is even more likely to cause a problem (but again, the problem will depend randomly on things like the link order, the compiler, the version number, the phase of the moon, etc.).

         #include "Fred.hpp"
         
         string x = Fred::g();
         

          На что похожи статические члены-функции?

          Static member functions are like services attached to the factory rather than services attached to the objects produced by the factory.

           #include <cstdlib>
           #include <iostream>
           using namespace std;
           
           class Car {
           public:
           
             Car() throw();
             Car(const Car& c) throw();
             // No need for an explicit assignment operator or destructor
             // since these don't create new Car objects.
           
             static int num() throw();                          <-- 1
             int odometer() const throw();                      <-- 2
             void drive() throw();                              <-- 3
           
           private:
             static int num_;                                   <-- 4
             int        miles_;                                 <-- 5
           };
           
           Car::Car() throw()
           : miles_(0)
           {
             cout << "Car ctor\n";
             ++num_;
           }
           
           Car::Car(const Car& c) throw()
           : miles_(c.miles_)
           {
             cout << "Car copy\n";
             ++num_;
           }
           
           int Car::num() throw()                               <-- 6
           { return num_; }
           
           int Car::odometer() const throw()
           { return miles_; }
           void Car::drive() throw()
           { ++miles_; }
           
           int Car::num_ = 0;                                   <-- 7
           

          (1) Class service

          (2) Object service

          (3) Object service: You drive a Car, not a factory

          (4) Class data

          (5) Object data

          (6) Should be in same source file as num_; see FAQ 16.04

          (7) Class data is automatically initialized, often before main()

          Some services make sense only when applied to an object.

           void fiddleWithObject(Car& car) throw()
           {
             while (rand() % 10 != 0)
               car.drive();
             cout << "car.odometer() = " << car.odometer() << '\n';
           }
           

          Some services make sense only when applied to the factory.

           void fiddleWithClass() throw()
           {
             cout << "Car::num() returns " << Car::num() << '\n';
           
             #ifdef GENERATE_ERROR
               Car::drive();                                    <-- 1
               Car::odometer();                                 <-- 2
             #endif
           }
           

          (1) ERROR: Can't drive a factory

          (2) ERROR: Factories don't have odometers

          Since the factory exists before it produces its first object, the factory can provide services before instantiating an object. That is, fiddleWithClass() can be called before the first Car object is created and/or after the last Car object is destructed:

           int main()
           {
             fiddleWithClass();
           
             {
               Car a;
               fiddleWithClass();
               fiddleWithObject(a);
             }
           
             fiddleWithClass();
           }
           

            Можно ли сравнить статические члены данных с глобальными переменными?

            A static data member is like a global variable with a funny name that does not need to be public:.

            If a class has a static data member, there is only one copy of that datum even if there are many instances of the class. This is like a global variable. The difference is that a static data member has a funny (scoped) name (it doesn't pollute the global namespace) and it needn't be public: (static data members can be private:, protected:, or public:).

            These factors allow classes to be the logical packaging device; source files are reduced to mere buckets of bits and code. There is no need to use source files for hiding data (for instance, there is no need to use file-scope static data to hide data in a source file) since the data can now be hidden in a class. This distinction allows the physical packaging of software to be different from the logical packaging. For example, physical packaging may be optimized based on page fault analysis or on compile-time performance or for maintainability, and so forth.

            Global data is rarely used any more. Normally objects and instance data work fine, but when true global data is required, the right choice is normally to use static member data (that is, class-scope static data members) or to put the data in an unnamed namespace.

              Можно ли сравнивать статические функции с дружескими?

              A static member function is like a friend function with a funny name that needn't be public:.

              Static member functions and top-level (C-like) friend functions are similar in that neither has an implicit this parameter, and both have direct access to the class's private: and protected: parts.

              Except for overloaded operators, most friend functions end up actually being static member functions, because static member functions have a scoped name (they don't pollute the global namespace) and they don't have to be public:they can also be private: or protected:.

                Что такое именованный конструктор?

                An idiom that allows a specific name for an operation that is similar to a constructor.

                Occasionally, classes have a large suite of overloaded constructors. Because all constructors for a class have the same name, it can be confusing to select between the various overloaded constructors. When this happens, the named constructor idiom may be appropriate.

                For example, consider a complex number class, Complex, that supports construction using either polar coordinates (magnitude, angle) or rectangular coordinates (real part, imaginary part). Unfortunately, these constructors are very similar; both constructors take two floats. Should Complex(2,1) be interpreted as specifying polar form ("2 at angle 1") or as specifying rectangular form ("2 plus 1 times the imaginary constant")?

                Many potential solutions exist to resolve this ambiguity. A boolean flag could indicate which is intended, or an extra dummy parameter on one of the constructors could be used to avoid runtime overhead by making the selection at compile time rather than at runtime. Another solution is to use the named constructor idiom, which is a way of using static member functions to provide alternative constructors for a class. Usually, the named constructor idiom results in user code that is more direct and readable:

                 #include <cmath>
                 #include <iostream>
                 using namespace std;
                 
                 class Complex {
                 public:
                   Complex(float real=0.0) throw();
                   static Complex rect(float real, float imag) throw();   <-- 1
                   static Complex polar(float mag,  float ang) throw();   <-- 1
                 private:
                   Complex(float real, float imag) throw();
                   float real_, imag_;
                 };
                 
                 inline Complex::Complex(float real) throw()
                 : real_(real)
                 , imag_(0)
                 { }
                 
                 inline Complex::Complex(float real, float imag) throw()
                 : real_(real)
                 , imag_(imag)
                 { }
                 
                 inline Complex Complex::rect(float real, float imag) throw()
                 { return Complex(real, imag); }
                 
                 inline Complex Complex::polar(float mag,  float ang) throw()
                 { return Complex(mag*cos(ang), mag*sin(ang)); }
                 

                (1) The so-called named constructors

                Both rect() and polar() are static member functions that operate like constructors. Users explicitly call whichever version they want.

                 int main()
                 {
                   Complex a;                                         <-- 1
                   Complex b = 3.14;                                  <-- 2
                   Complex c = Complex::rect(3,2);                    <-- 3
                   Complex d = Complex::polar(3,2);                   <-- 4
                 }
                 

                (1) real part=0, imag part=0

                (2) real part=3.14, imag part=0

                (3) real part=3, imag part=2

                (4) magnitude=3, angle=2 radians

                  Как нужно вызывать статическую функцию?

                  Explicitly name the class using ::.

                  For documentation purposes, calls to static member functions should be coded as Classname::staticMember() rather than as object.staticMember() or ptr->staticMember(). The :: is a reminder that the member function is statically bound (see FAQ 21.09) and that the member function is attached to the class rather than to an individual object of the class.

                  Calling a static member function Classname::f() from another member function of class Classname is an exception to this rule. In this case, the call can be simply f(), since the meaning is usually clear in this context. For example, when Classname::f() is a protected: static member function of the class, simply write f() rather than Classname::f().

                    Почему класс со статическими функциями может вызывать ошибку при линковке?

                    Static data members must be explicitly defined in exactly one source file.

                    Here's an example of a header file, such as Fred.hpp.

                     class Fred {
                     public:
                       static int x_;                                     <-- 1
                     };
                     

                    (1) Declare (not define) static member Fred::x_

                    The linker generates an error ("Fred::x_ is not defined") unless (exactly) one of the source files defines Fred::x_. This definition is normally done in the class's source file, such as file Fred.cpp:

                     #include "Fred.hpp"
                     int Fred::x_ = 42;                                   <-- 1
                     

                    (1) Define static member Fred::x_

                    Note that the explicit initializer (= 42 in the example) is optional. That is, the line could be changed to

                     int Fred::x_;                                        <-- 1
                     

                    (1) Initialize Fred::x_ to 0

                    Note that even when the static data member is private: or protected:, it must still be explicitly defined as shown in one of the two examples.

                      Как инициализировать const статический член данных?

                      A const static data member is declared in the class and is normally defined (and initialized) in a source file, such as a .cpp file. But in some cases it can be initialized in the class body proper. For example, integral types, such as int, unsigned long, char, and so on, are special: they can be initialized where they are declared in the class body proper.

                      Here is a sample header file, Fred.hpp.

                       #include <string>
                       using namespace std;
                       class Barney { };
                       
                       class Fred {
                       public:
                         // ...
                       private:
                         static const int i_ = 42;                          <-- 1
                         static const char c_ = 'z';                        <-- 1
                         static const float x_;                             <-- 2
                         static const string s_;                            <-- 2
                         static const Barney b_;                            <-- 2
                       };
                       

                      (1) Integral data types can be initialized in the class body proper

                      (2) Nonintegral data types must be defined in the source file, not in the class body proper

                      Here is corresponding source file, Fred.cpp.

                       #include "Fred.hpp"
                       
                       const float Fred::x_ = 3.14;
                       const string Fred::s_ = "Hello";
                       const Barney Fred::b_;
                       

                      Another common style is to use anonymous (unnamed) enums. This style is no longer needed, but it is typical in older C++ code. For example, the static const int i_ = 42 from the previous example can be replaced by enum { i_ = 42 }, as shown in the following example.

                       class Fred {
                       public:
                         // ...
                       private:
                         enum { i_ = 42 };                                  <-- 1
                         // static const int i_ = 42;                       <-- 2
                       };
                       

                      (1) Older style

                      (2) Newer style

                      In either case, the constant is called Fred::i_, and it can be private:, protected:, or public:.

                        FAQ 16.12 What is the right strategy for implementing a function that needs to maintain state between calls?

                        Turn the function into a functionoid. Do not create a function with local static data.

                        In C, it was common to create a function that maintained state between calls by means of local, static data inside the function body. Since this is unsafe in a multithreaded environment, in C++ such a function should be implemented as a functionoid, which is a fancy name for a class that has one major member function. The local static data from the original C-like function should become nonstatic member data of the functionoid class. The benefit is to allow different callers to have different values for the datum that used to be static. For example, every calling function that wants its own copy of the function's state can simply create its own distinct functionoid object.

                        Viewing a function with local static data as a global functionoid object makes it clear why the static data is expensive to model (global variables aren't fun!). For example, consider the rand() function, which remembers some state between calls:

                         int rand() throw()
                         {
                           static unsigned long current = 1001;               <-- 1
                           current = current * 22695477UL + 1;
                           return int(current >> 12) & 0x7fff;
                         }
                         

                        (1) Bad form: Local static data

                        The static variable current introduces subtle dependencies between users of the function. Any change in the calling pattern can alter the behavior of this routine. Such routines are notorious in shared-memory, multithreaded environments.

                        A better way to do this is with a class. Every user function that wants a pseudorandom stream of numbers can create its own object of this class.

                         #include <iostream>
                         using namespace std;
                         
                         class RandomSequence {                               <-- 1
                         public:
                           RandomSequence(int initialSeed=1001) throw();
                           int next() throw();
                         protected:
                           unsigned long current_;
                         };
                         RandomSequence::RandomSequence(int initialSeed) throw()
                         : current_(initialSeed) { }
                         
                         int RandomSequence::next() throw()
                         {
                           current_ = current_ * 22695477UL + 1;
                           return int(current_ >> 12) & 0x7fff;
                         }
                         

                        (1) Good form: Functionoid

                        The user gets a sequence of random numbers by using the member function next().

                         void printRandomSequence() throw()
                         {
                           RandomSequence rand;
                           for (int i = 0; i < 10; ++i)
                             cout << rand.next() << ' ';
                         }
                         
                         int main()
                         { printRandomSequence(); }
                         

                        Before, there was a global rand() function with a single state variable. Now there is a local rand object and as many state variables as there are user functions that want an independent pseudorandom sequence. The dependencies among callers (and especially among the various threads) are eliminated at the source. There is no more shared static data.

                        Another reason to create a functionoid object is when a function performs several distinct operations. In C, such a function would often accept a what-to-do parameter that selected the operation to be performed. In C++, such a multioperation function should be implemented as an object. Each distinct operation performed by the original function should become a distinct member function on the object. Such an object is also called a functionoid.

                          FAQ 16.13 How can the function call operator help with functionoids?

                          The function call operator lets users pretend that the functionoid is a function.

                          In the previous example, class RandomSequence is a functionoid. Unlike a standard function, RandomSequence can maintain state between calls without sharing that state between all of its callers.

                          Functionoids often use the function call operator (operator()()) rather than a named member function such as next(). In the following code, next() has been replaced by operator()() in class RandomSequence.

                           #include <iostream>
                           using namespace std;
                           
                           class RandomSequence {
                           public:
                             RandomSequence(int initialSeed=1001) throw();
                             int operator()() throw();                          <-- 1
                           protected:
                             unsigned long current_;
                           };
                           
                           RandomSequence::RandomSequence(int initialSeed) throw()
                           : current_(initialSeed) { }
                           int RandomSequence::operator()() throw()
                           {
                             current_ = current_ * 22695477UL + 1;
                             return int(current_ >> 12) & 0x7fff;
                           }
                           

                          (1) The name of the member function is operator()

                          Given an object of class RandomSequence called rand, users can now use rand() instead of rand.next():

                           int main()
                           {
                             RandomSequence rand;
                             for (int i = 0; i < 10; ++i)
                               cout << rand() << ' ';
                           }
                           

                            FAQ 16.14 Is it safe to be ignorant of the static initialization order problem?

                            No, ignorance of the static initialization order problem can result in application crashes.

                            The static initialization order problem has to do with the lifetimes of class-scope static objects and file-scope or namespace-scope objects. These objects are constructed near the beginning of the application's execution (often before main() begins) and are destructed after main() finishes. The nightmare scenario occurs when there is an order dependency between initializations across different compilation units (that is, different .cpp files). This can be both dangerous and subtle.

                            For example, suppose a constructor of class Fred uses a static data member of class Wilma, and a user creates a global Fred object. If the static objects in the user's source file are initialized before those in the source file containing Fred's static data member, Fred's constructor will access a Wilma object before it is constructed.

                            Although this description sounds uncommon, it actually shows up quite often in practice, especially with factory objects whose constructor registers something in a "registry" object. For example, Wilma is actually a map (a registry object) and Fred is a "factory" object whose constructor registers something in the map.

                            In the following example, the order of the global Fred and the static data member have been arranged to simulate this disaster.

                             #include <iostream>
                             using namespace std;
                             
                             class Wilma {
                             public:
                               Wilma()  throw();
                               void f() throw();
                             };
                             
                             inline Wilma::Wilma()  throw() { cout << "Wilma ctor\n"; }
                             inline void Wilma::f() throw() { cout << "Wilma used\n"; }
                             
                             class Fred {
                             public:
                               Fred() throw();
                             protected:
                               static Wilma wilma_;
                             };
                             
                             inline Fred::Fred() throw()
                             {
                               cout << "Fred ctor\n";
                               wilma_.f();
                             }
                             
                             Fred x;
                             Wilma Fred::wilma_;
                             
                             int main()
                             { }
                             

                            The (annotated) output from this program shows that the Wilma object is used before it is initialized. This is a disaster.

                             Fred ctor
                             Wilma used                                           <-- 1
                             Wilma ctor                                           <-- 2
                             

                            (1) The static object is used

                            (2) The static object is constructed

                              FAQ 16.15 What is a simple and robust solution to the static initialization order problem?

                              A very simple and fairly robust solution is to change the static data member into a static member function that returns a reference to a dynamically allocated object. This provides construct on first use semantics, which is desirable in many situations.

                              The following code shows how to apply this technique to the example from the previous FAQ. The static data member Wilma Fred::wilma_ has been changed to a static member function, Wilma& Fred::wilma(), and all uses of Fred::wilma_ have been changed to Fred::wilma(). Class Wilma is not shown since it is unchanged from the example in the previous FAQ.

                               class Fred {
                               public:
                                 Fred() throw();
                               protected:
                                 static Wilma& wilma() throw();                     <-- 1
                               };
                               
                               inline Fred::Fred() throw()
                               {
                                 cout << "Fred ctor\n";
                                 wilma().f();                                       <-- 2
                               }
                               
                               Fred x;
                               
                               Wilma& Fred::wilma() throw()                         <-- 3
                               
                               {
                                 static Wilma* p = new Wilma();                     <-- 4
                                 return *p;
                               }
                               

                              (1) Used to be static Wilma wilma_;

                              (2) Used to be wilma_.f()

                              (3) Used to be Wilma Fred::wilma_;

                              (4) Don't forget the "static"!

                              In the static member function Fred::wilma(), pointer p is static, so the new Wilma() object is allocated only the first time that Fred::wilma() is called. All subsequent calls simply return a reference to the same Wilma object.

                              As shown in the (annotated) output from this program, the Wilma object is initialized before it is used. This is good.

                               Fred ctor
                               Wilma ctor                                           <-- 1
                               Wilma used                                           <-- 2
                               

                              (1) The static object is constructed

                              (2) The static object is used

                                FAQ 16.16 What if the static object's destructor has important side effects that must eventually occur?

                                One limitation of the technique described in the previous FAQ is that it abandons the static Wilma object on the heapthe Wilma object is never destructed. If the Wilma object's destructor has important side effects that should eventually happen, then the implementation of Fred::wilma() needs to be changed so that it simply returns a local static object by reference.

                                The following code shows how to apply this technique to the example from the previous FAQ. The local static pointer static Wilma* p = new Wilma(); has been changed to simply static Wilma w;, and the return statement simply returns the local static object w. Class Wilma is not shown since it is unchanged from the example in the previous FAQ.

                                 class Fred {
                                 public:
                                   Fred() throw();
                                 protected:
                                   static Wilma& wilma() throw();                     <-- 1
                                 };
                                 
                                 inline Fred::Fred() throw()
                                 {
                                   cout << "Fred ctor\n";
                                   wilma().f();                                       <-- 2
                                 }
                                 
                                 Fred x;
                                 Wilma& Fred::wilma() throw()
                                 {
                                   static Wilma w;                                    <-- 3
                                   return w;
                                 }
                                 

                                (1) Same as in the previous FAQ

                                (2) Same as in the previous FAQ

                                (3) Used to be static Wilma* p = new Wilma();

                                Since the local static object w is static, it is initialized only the first time control flows over its declaration, that is, the first time Fred::wilma() is called. This is the same construct on first use semantics as was described in the previous FAQ, which is normally quite desirable.

                                Unfortunately, this solution has its own problems. Remember why this solution was proposed in the first place: the Wilma object's destructor has important side effects that need to eventually occur. Although this second solution guarantees that they will occur (assuming the Fred::wilma() function is called at least once), it introduces a new problem that the previous solution did not have: a static deinitialization order problem. In particular, if some static object's destructor calls Fred::wilma(), Murphy's Law says that that call will occur after the static Wilma object has been destructed. If that may occur, the best solution is the nifty counter technique, which is described in the next FAQ.

                                  FAQ 16.17 What if the static object's destructor has important side effects that must eventually occur and the static object must be accessed by another static object's destructor?

                                  This is the most restrictive of all scenarios since it means that the construction must occur before the object is first used, and it must be destructed after its last use. The solution is called the nifty counter technique. What happens is that a static counter is created (the nifty counter) along with a static object in each source file whose constructor increments this nifty counter and whose destructor decrements the nifty counter. When the nifty counter is incremented from zero, the static object is initialized, and when the nifty counter is decremented to zero, the static object is destructed.

                                  The following code shows how to apply this technique to the example from the previous FAQ. The static data member is back, but this time it is a static pointer. Nested class Fred::Init has a static counter (the nifty counter) called count_, which is incremented by Fred::Init's constructor and decremented by Fred::Init's destructor. The Fred::wilma_ object is created when the nifty counter is incremented from zero and is destructed when the counter is decremented back to zero. Class Wilma is not shown since it is unchanged from the example in the previous FAQ.

                                  Here is the header file Fred.hpp.

                                   class Fred {
                                   public:
                                     Fred() throw();
                                   protected:
                                     class Init;                                        <-- 1
                                     friend Init;                                       <-- 2
                                     static Wilma* wilma_;                              <-- 3
                                   };
                                   
                                   class Fred::Init {
                                   public:
                                     Init()  throw() { if (count_++ == 0) wilma_ = new Wilma(); }
                                     ~Init() throw() { if (--count_ == 0) delete wilma_; }
                                   private:
                                     static unsigned count_;
                                   };
                                   
                                   static Fred::Init fredInit;                          <-- 4
                                   
                                   inline Fred::Fred() throw()
                                   {
                                     cout << "Fred ctor\n";
                                     wilma_->f();                                       <-- 5
                                   }
                                   

                                  (1) This declares nested class Fred::Init

                                  (2) So Fred::Init can access Fred::wilma_

                                  (3) This is now a static pointer

                                  (4) The key: a static Fred::Init object defined in the header file

                                  (5) This is now safe

                                  Here is the source file "Fred.cpp":

                                   #include "Fred.hpp"
                                   
                                   unsigned Fred::Init::count_ = 0;
                                   Wilma* Fred::wilma_ = NULL;
                                   

                                  Every source file that includes header file Fred.hpp ends up with its own static Fred::Init object called fredInit. Since this static object appears very early in the source file, it is initialized before most other static objects in the source file (in particular, it is guaranteed to be initialized before any static object in the source file could call Fred::Fred(), since the call to any member function of class Fred can occur only after the header of class Fred has been #included).

                                  Of all the source files that include header file Fred.hpp, one of them, say foo.cpp, is initialized first. During the static initialization of foo.cpp, the nifty counter Fred::Init::count_ is incremented from zero, and the Fred::wilma_ object is created. Since the Fred::wilma_ object is initialized before any calls to any member functions of class Fred can be made, it is guaranteed to be constructed before it is used.

                                  The static deinitialization situation is similar but opposite. Of all the source files that include Fred.hpp, one of them, say foo.cpp, is the last one to be deinitialized. Since deinitialization occurs in bottom to top order, the static Fred::Init object in file foo.cpp is one of the last things that is destructed (certainly it is destructed after any static object could call any member function of class Fred). Therefore the Fred::wilma_ object is destructed just after the last static object could possibly use it: it will not be used after it has been destructed.

                                  Unfortunately the nifty counter technique also has problems. Although it never allows an object to be used either before construction or after destruction, it does force a small amount of static initialization code into every source file that includes header file Fred.hpp. This means that a large percentage of the application needs to be paged into memory during startup, which can significantly degrade startup performance, especially if there are a lot of source files that include headers that use the nifty counter technique.

                                    FAQ 16.18 What are some criteria for choosing between all these various techniques?

                                    Here are the pros and cons of each of the four techniques that were presented.

                                    • Construct on first use with new: Users access the static object via a non-inline access function. The access function has a local static pointer and allocates the object via new. Pro: The technique is easy to remember, simple to use, efficient during startup, safe during startup, and safe during shutdown. Con: The static object is abandoned on the heapif the static object's destructor has important side effects that must occur, this technique cannot be used. (See FAQ 16.15.)

                                    • Construct on first use with a local static object: Users access the static object via a non-inline access function. The access function has a local static object. Pro: The technique is easy to remember, simple to use, efficient during startup, and safe during startup, and it eventually destructs the object. Con: The technique is not safe during shutdownif a static object's destructor calls the access function, the static object could be accessed after it is destroyed, which would be a disaster. (See FAQ 16.16.)

                                    • Construct before first use with a class-scope static object: Users access the static object via a non-inline access function. The access function is not inline and is defined in the same file that defines the static object. Pro: The technique is easy to remember, simple to use, efficient during startup, and safe during startup, and it eventually destructs the object. Con 1: The technique is not safe during shutdownif a static object's destructor calls the access function, the static object could be accessed after it is destroyed, which would be a disaster. Con 2: the technique doesn't enforce the constraint that the object be accessed only via the access function. (See FAQ 16.16.)

                                    • Nifty counter: The static object is constructed when the nifty counter is incremented from 0 and is destructed when the nifty counter is decremented to 0. Pro: The technique is simple to use, safe during startup, and safe during shutdown, and it eventually destructs the object. Con: Potential performance problem during startup and shutdown. (See FAQ 16.17.)

                                    In our experience, a significant percentage of static objects that should be constructed before they are first used are registry objects (for example, a map object that will be populated during static initialization by other static objects). In most of these cases, the first technique is sufficient since the map object rarely has to be destructedit can be abandoned on the heap. This is good news because the first technique is easy to use, easy to remember, is fast, and is safe during both startup and shutdown.

                                      FAQ 17.01 What is the purpose of this chapter?

                                      This chapter discusses derived classes and inheritance at the programming language level. Part II discusses the same information on a design level and should be read before this chapter. This chapter dwells only on public, single, nonvirtual inheritance, which is the most prevalent form. Chapter 38 discusses some of the other types of inheritance.

                                        FAQ 17.02 How does C++ express inheritance?

                                        Here is a typical C++ inheritance declaration.

                                         #include <iostream>
                                         using namespace std;
                                         
                                         class Vehicle {
                                         public:
                                           virtual void startEngine() throw() = 0;
                                           virtual ~Vehicle() throw();
                                         };
                                         
                                         Vehicle::~Vehicle() throw()
                                         { }
                                         class V8Engine {
                                         public:
                                           void start() throw();
                                         };
                                         
                                         void V8Engine::start() throw()
                                         { cout << "starting V8Engine\n"; }
                                         
                                         class Car : public Vehicle {
                                         public:
                                           virtual void startEngine() throw();
                                         protected:
                                           V8Engine engine_;
                                         };
                                         
                                         void Car::startEngine() throw()
                                         { engine_.start(); }
                                         

                                        This relationship can be described in several equivalent ways.

                                        • Car is a kind-of Vehicle.

                                        • Car is derived from Vehicle.

                                        • Car is a subclass of Vehicle.

                                        • Car is a child class of Vehicle (not common in the C++ community).

                                        • Vehicle is the base class of Car.

                                        • Vehicle is the parent class of Car (not common in the C++ community).

                                        • Vehicle is the super-class of Car (not common in the C++ community).

                                        As a consequence of the kind-of relationship, a Car object can be treated as a Vehicle object. For example, since function f(Vehicle&) accepts any kind-of Vehicle, it can be passed a Car or an object of any other class derived from Vehicle:

                                         void f(Vehicle& v) throw()
                                         { v.startEngine(); }
                                         
                                         int main()
                                         {
                                           Car c;
                                           f(c);
                                         }
                                         

                                        UML uses the following notation to show inheritance.

                                        graphics/17fig01.gif

                                          FAQ 17.03 What is a concrete derived class?

                                          A concrete derived class is a derived class that has no pure virtual functions.

                                          Because an abstract class cannot be instantiated directly, one or more derived classes are normally defined as implementations of the abstraction provided by the abstract class. A concrete derived class simply provides definitions for all its inherited pure virtual functions. If a definition for one of the inherited pure virtual functions is forgotten, any attempt to instantiate the class results in the compiler issuing an error message.

                                          Abstract base class Base has two pure virtual member functions f() and g(), and derived class Derived provides a definition for f() but not for g(). Therefore Derived is also abstract:

                                           class Base {
                                           public:
                                             virtual void f() throw() = 0;
                                             virtual void g() throw() = 0;
                                           };
                                           
                                           class Derived : public Base {
                                           public:
                                             virtual void f() throw();
                                           };
                                           
                                           void Derived::f() throw()
                                           { }
                                           

                                          If Derived2 were derived from Derived and Derived2 had to be concrete, Derived2 would have to provide a definition for g(), but it wouldn't be required to override Derived::f(). This is shown in the following example.

                                           class Derived2 : public Derived {
                                           public:
                                             virtual void g() throw();
                                           };
                                           void Derived2::g() throw()
                                           { }
                                           
                                           void sample() throw()
                                           { Derived2 x; }                                      <-- 1
                                           

                                          (1) OK: Derived2 is a concrete class

                                            FAQ 17.04 Why can't a derived class access the private: members of its base class?

                                            A derived class can't access the private: members of its base class because the base class intentionally hides some of its implementation details from its derived classes.

                                            Suppose class Fred contains a member datum or member function that is likely to change. Unless derived classes need to access this member, the base class Fred would be wise to declare the member as private:. This reduces the ripple effect of changes in the base class. For example, the private: member can be removed or modified without fear of breaking derived classes. That is, the derived classes are protected from rewrites whenever the semantics or even existence of the private: member change.

                                            For example, class Wilma below can access publ_ and prot_, but cannot access priv_.

                                             #include <iostream>
                                             using namespace std;
                                             
                                             class Fred {
                                             public:
                                               Fred() throw();
                                               int publ_;
                                             protected:
                                               int prot_;
                                             private:
                                               int priv_;
                                             };
                                             
                                             Fred::Fred() throw()
                                             : publ_(1)
                                             , prot_(2)
                                             , priv_(3)
                                             { }
                                             
                                             class Wilma : public Fred {
                                             public:
                                               void printem() const throw();
                                             };
                                             
                                             void Wilma::printem() const throw()
                                             {
                                               cout << publ_ << '\n';                             <-- 1
                                               cout << prot_ << '\n';                             <-- 2
                                             
                                               #ifdef GENERATE_ERROR
                                                 cout << priv_ << '\n';                           <-- 3
                                               #endif
                                             }
                                             
                                             int main()
                                             {
                                               Wilma a;
                                               a.printem();
                                             }
                                             

                                            (1) OK: Derived can access base's public: stuff

                                            (2) OK: Derived can access base's protected: stuff

                                            (3) Error: Derived cannot access base's private: stuff

                                            The designer of a base class gives derived classes access to implementation details by declaring them as protected:.

                                              FAQ 17.05 How can a base class protect derived classes so that changes to the base class will not affect them?

                                              The easiest solution is to define a protected: interface in addition to the public: interface.

                                              A class hierarchy is more resilient to changes if it has two distinct interfaces for two distinct sets of users.

                                              • Its public: interface serves unrelated classes.

                                              • Its protected: interface serves derived classes.

                                              Both interfaces must be fully specified. For instance, the actual raw data of a class could be private: with a set of protected: inline member functions for accessing this data. These inline member functions define an interface between the derived classes and the raw bits of the base class. Then the private: data of the base class could be changed within reasonable bounds without affecting the derived classes. It would still be necessary to recompile the derived classes after a change to the base class, though the source code of the derived class would not need to be changed unless the protected: interface is modified in a nonbackward compatible manner.

                                              For example, suppose class Base has an int data member. Base can ensure that derived classes do not rely on the specific data structure by making the data structure private: (in this case, a simple int) and defining inline protected: members for accessing these data. Derived class Derived accesses the value using these protected: inline member functions.

                                               class Base {
                                               public:
                                                 Base() throw();
                                               protected:
                                                 void storeValue(int value) throw();
                                                 int  retrieveValue() const throw();
                                               private:
                                                 int value_;
                                               };
                                               
                                               inline Base::Base()                     throw() : value_(37) { }
                                               inline void Base::storeValue(int value) throw() { value_ = value; }
                                               inline int  Base::retrieveValue() const throw() { return value_; }
                                               
                                               class Derived : public Base {
                                               public:
                                                 void f(int i) throw();
                                               };
                                               
                                               void Derived::f(int i) throw() { storeValue(i); }
                                               
                                               int main()
                                               {
                                                 Derived d;
                                                 d.f(42);
                                               }
                                               

                                                FAQ 17.06 Can a derived class pointer be converted into a pointer to its public base class?

                                                Such conversions are possible and don't even require a pointer cast.

                                                A publicly derived class is a kind-of its base class. By implication, the upward conversion is perfectly safe, and is quite common. For example, a pointer to a Car is in fact already pointing at a Vehicle, since a Car is a kind-of a Vehicle.

                                                 class Vehicle { };
                                                 class Car : public Vehicle { };
                                                 
                                                 void f(Vehicle* v) throw();
                                                 
                                                 void g(Car* c) throw()
                                                 {
                                                   f(c);  //Perfectly safe; no cast needed
                                                 }
                                                 

                                                  FAQ 17.07 How can a class Y be a kind-of another class X as well as getting the bits of X?

                                                  This is easy: use public inheritance.

                                                  Here is the C++ syntax for public inheritance.

                                                   class X            { /*bits and/or code go here*/ };
                                                   class Y : public X { /*more bits and/or code go here*/ };
                                                   

                                                  This does two distinct things. First, it provides the kind-of relationship: Y is a kind-of X, therefore Y supports the same services as X (Y might add some new member functions as well). Second, it shares bits and code: Y inherits X's bits (data structures) and code (algorithms).

                                                    FAQ 17.08 How can a class Y get the bits of an existing class X without making Y a kind-of X?

                                                    There are three alternatives. The preferred solution is normal composition, also known as has-a. But in some cases private inheritance should be used, and in a few cases, protected inheritance should be used.

                                                    Here is the class X that will be used in each of the three following examples.

                                                     class X {
                                                     public:
                                                       void f() throw();
                                                       void g() throw();
                                                     private:
                                                       int a_;
                                                       float b_;
                                                     };
                                                     

                                                    Here is the C++ syntax for composition (that is, Y has-a X). This is the preferred solution.

                                                     class Y1 {
                                                     public:
                                                       void f() throw();
                                                     protected:
                                                       X x_;                                              <-- 1
                                                     };
                                                     
                                                     void Y1::f() throw()
                                                     { x_.f(); }                                          <-- 2
                                                     

                                                    (1) Composition: Y1 is not a kind-of X

                                                    (2) Y1 calls member functions in X ("reuse")

                                                    Here is the C++ syntax for private inheritance, which is semantically the same as has-a but with an increased ripple effect (changes to the protected: part of X can break the private derived class Y2). This is the second of the three alternatives.

                                                     class Y2 : private X {                               <-- 1
                                                     public:
                                                       using X::f;                                        <-- 2
                                                     };
                                                     

                                                    (1) Private inheritance: Y2 is not a kind-of X

                                                    (2) Same semantics as above: Calls to Y2::f() end up in X::f()

                                                    Here is the C++ syntax for protected inheritance, which is semantically the same as has-a but with an even greater ripple effect than private inheritance (changes to the protected: part of X can break the protected derived class Y3 and can also break any classes derived from Y3). This is the last of the three alternatives.

                                                     class Y3 : protected X {                             <-- 1
                                                     public:
                                                       using X::f;                                        <-- 2
                                                     };
                                                     

                                                    (1) Protected inheritance: Y3 is not a kind-of X

                                                    (2) Same semantics as above: Calls to Y3::f() end up in X::f()

                                                    In all three cases, a Y object has a X object, and users of Y are unaware of any relationship between Y and X. For example, user code will not break if the relationship between Y and X changesor even is eliminated. See FAQ 37.01 for more information on private and protected inheritance.

                                                      FAQ 17.09 How can a class Y be a kind-of another class X without inheriting the bits of X?

                                                      It's doable, but C++ has to be outfoxed.

                                                      The only mechanism provided by C++ that defines the kind-of relationship also forces inheriting the bits of the base class. If the base class's data structures are inappropriate for certain derived classes, it's necessary to outfox C++ by deferring the definition of the bits to the lower levels of the class hierarchy.

                                                      One way to do this is to define an ABC that possesses no (or few) internal data structures, then define the data structures in the concrete derived classes. In this way, the derived classes define the kind-of relationship, but they don't have any bits imposed on them by the base class. For example, both X and Y would inherit from this common ABC, but they would not inherit from each other. Strictly speaking, this does not achieve the original goal (Y is not a kind-of X), but it is normally close enough. The reason is that the bulk of the system can (hopefully) be written to use the ABC rather than X or Y directly, and inheritance allows both X and Y to be passed into the bulk of the system.

                                                        FAQ 18.01 What is the purpose of this chapter?

                                                        This chapter presents the three types of access control in C++ classes. Access controls allow the programmer to declare a class's members as public:, protected:, and/or private:.

                                                          FAQ 18.02 How are private:, protected:, and public: different?

                                                          The situation is similar to personal secrets (shared only with friends), family secrets (shared with friends and children), and nonsecrets (shared with anybody), respectively.

                                                          A private: member of a class is accessible only by members and friends of the class.

                                                          A protected: member of a class is accessible by members and friends of the class and by members and friends of derived classes, provided they access the base member via a pointer or a reference to their own derived class.

                                                          A public: member of a class is accessible by everyone.

                                                          There is no difference between the access rules for data members and member functions. "Members and friends of class X" include member functions of class X, friend functions of class X, and member functions of friend classes of class X.

                                                            FAQ 18.03 Why can't subclasses access the private: parts of their base class?

                                                            The base class encapsulation has to protect itself from being undermined.

                                                            Suppose a subclass could access the private: portion of its base class. Would it make sense if just anyone could take away the option for the base class developer to change internal mechanisms in the future because they had subclassed a base class and locked in on its implementation? That would not be in anyone's best interests. So there is a need for ways to distinguish between

                                                            • Mechanisms and services that are available to everyone, the public: interface

                                                            • Mechanisms and services that are available only to subclasses, the protected: interface

                                                            • Mechanisms and services that are reserved for change without concern for breaking user code, the private: interface

                                                            Note that this is not a security issue. Developers can always look at the header files to see what is going on. The key notion is that the designer makes different promises to different audiences, and access controls provide a way to do that.

                                                            Also, notice that a subclass can access the protected: portion of its base class only when it acts specifically as a subclass. It cannot access the protected: portion of an object of its base class that is freestanding or of a different derived class.

                                                              FAQ 18.04 What's the difference between the keywords struct and class?

                                                              The difference is mostly perception.

                                                              From a pure language perspective, the major difference between the keywords struct and class is the default access level assigned to members and base classes. The default access level assigned to members and base classes of a struct is public:, while the default access level for members and base classes of a class is private:. Regardless, it is best to put an explicit public, private, or protected in the base class specifications, and it is usually best for the class to start with an explicit public:. With that approach, these defaults are of little consequence in practice.

                                                              The perception, however, is very different. A struct is perceived as an open bucket of bits. Most structs have very few member functions (often they have only a constructor), and they are often entirely public:.

                                                                FAQ 18.05 When should a data member be protected: rather than private:?

                                                                Some authors discourage protected: data in all cases on the grounds that it creates a stronger coupling with the derived class. For example, if a derived class were written by a customer or some other third party, changing the protected: data could break the derived class's code. In these situations, it is far better to create a protected: access function to the private: data rather than to allow direct access to protected: data by derived classes.

                                                                However, one size does not fit all. Although there are situations when third parties create derived classes, there are also many situations when they do not. From a practical standpoint, an organization often has a very well-defined notion of which classes will be inherited from by third parties, which classes will be inherited from internally, and which classes will not be inherited from at all. Those who erroneously believe that inheritance is for code reuse will be alarmed at that last statement, but when proper inheritance is practiced, inheritance is planned and prepared for ahead of time; it is not normally something programmers stumble into. (See FAQs 7.01, 8.12.)

                                                                  FAQ 18.06 Why is private: the default access level for a class?

                                                                  The default assumption makes small programs easier to read. For example, when a member function is defined within the class body proper (a practice that we are not advocating), it is easier to understand the code if the data appears before the code. Note that the compiler finds the class's data member independent of whether it appears before or after the usage, but human readers don't usually do a two-pass scan. Since the data is often private:, the default access level makes it a tiny bit easier when functions are defined within the class body proper. For example, this is particularly valuable for textbook examples that define member functions within their class to save presentation space.

                                                                  But typical development efforts separate a lot of the implementation into a distinct file, such as a .cpp file. Thus, the problem with the member function coming before the data doesn't exist, and there is more reason to put the public: portion first. This lets the human reader see the interface portion of the class without having to wade through irrelevant implementation detail.

                                                                  Different authors take different approaches to this issue, but we probably all subscribe to the same guiding principle. Any differences are due to our audiences and presentation style rather than philosophies.

                                                                    FAQ 19.01 What is a friend?

                                                                    A friend is an entity to which a class grants access authority.

                                                                    Friends can be functions, other classes, or individual member functions of other classes. Friend classes are used when two or more classes are designed to work together and need access to each other's implementation in ways that the rest of the world shouldn't be allowed to have. In other words, they help keep private things private. For instance, it may be desirable for class DatabaseCursor to have more privilege to the internals of class Database than main() has.

                                                                    In the early days of OO, people had all sorts of strange ideas. Some people thought that the class was the fundamental unit that had to be encapsulated and concluded that friendship was evil. Experience has shown that some abstractions require more than one class, and that the abstraction needs the encapsulation barriers more than the component classes. As long as they are used properly, friends enhance the encapsulation of abstractions.

                                                                    Friend classes normally imply that one abstraction (a database with multiple cursors, for example) is implemented using several distinct classes (Database and DatabaseCursor). Using several classes to implement one abstraction is especially useful when the various classes have different lifetimes or different cardinalities. For example, it is likely that there are an arbitrary number of DatabaseCursor objects for any given Database object.

                                                                     #include <stdexcept>
                                                                     #include <string>
                                                                     using namespace std;
                                                                     
                                                                     class BTree { /*...*/ };
                                                                     class Cache { /*...*/ };
                                                                     class DatabaseCursor;
                                                                     
                                                                     class Database {
                                                                     public:
                                                                       unsigned numRecords() const throw();
                                                                     protected:
                                                                       friend DatabaseCursor;   // Grant access to DatabaseCursor
                                                                       BTree btree_;
                                                                       Cache cache_;
                                                                     };
                                                                     
                                                                     class DatabaseCursor {
                                                                     public:
                                                                       string getCurrentRecord() const throw(out_of_range);
                                                                       void   changeCurrentRecord(const string& record)
                                                                                                             throw(out_of_range);
                                                                       void   goToNextRecord() throw(out_of_range);
                                                                     protected:
                                                                       Database* db_;                                     <-- 1
                                                                                                                          <-- 2
                                                                     };
                                                                     

                                                                    (1) The Database to which this cursor object is attached

                                                                    (2) Pointers into the BTree and/or Cache go here

                                                                    It would be a bad idea to force class Database and class DatabaseCursor into one class by moving the member functions of DatabaseCursor into class Database (so we would now have Database::getCurrentRecord()). This would impose a one-cursor-per-database policy and would cause Database to manage both the data and a current position within that data.

                                                                    The UML diagram for this friendship relationship follows.

                                                                    graphics/19fig01.gif

                                                                      FAQ 19.02 What's a good mental model for friend classes?

                                                                      A secret handshake or other technique to exchange information with a confidant in such a way that normal people are unable to access the same secrets. Friend classes prevent users (the normal people) from seeing the information being exchanged through the secret codes that connect the various objects (confidants).

                                                                      The overall effect is to keep private things private.

                                                                      The alternative to granting special access privileges between the classes would be for the classes to declare public: member functions that allow anyone to manipulate the class's private: members. For instance, in the DatabaseCursor example, class Database would need to provide public: member functions to manipulate its cache, B-tree, file system, and so on. Although the implementation bits would be encapsulated, the implementation technique would be exposed. Subsequent changes to the implementation technique would break users' code.

                                                                      In the traditional software realm, friendship is called tight cohesion, and is, within limits, considered good.

                                                                        FAQ 19.03 What are some advantages of using friend classes?

                                                                        Friend classes are useful when a class wants to hide features from users that are needed only by another, tightly coupled class. Compared to making a member public:, it is sometimes better to make the member private:, which eliminates potential misuse by unknown users, and grant friendship status to the tightly cohesive class, thereby keeping implementation details hidden from the rest of the world.

                                                                        Friend classes also arise when a member function on a class needs to maintain state between calls and when multiple copies of this state must exist. Under these circumstances, the member function becomes a friend class, and the multiple copies of the state become multiple objects of the friend class.

                                                                          FAQ 19.04 Do friends violate the encapsulation barrier?

                                                                          Not necessarily.

                                                                          If the encapsulation barrier is narrowly defined as the suite of member functions on a class, then friends violate the encapsulation barrier. However, this is a naive view of encapsulation, and applying it consistently actually degrades the overall encapsulation of a system. For example, if another entity needs to be part of the same abstraction, this naive approach suggests that the first class should expose its implementation technique via an unnecessarily large suite of get/set member functions.

                                                                          The enlightened view is that the encapsulation barrier encapsulates an abstraction, not just a class. For example, the earlier example of a database with multiple cursors illustrates an abstraction that is too rich to be implemented by a single class. In cases like this, friend classes are a valuable way of hiding the (possibly complex) interrelationships between the various pieces of the abstraction.

                                                                          Friends don't violate the encapsulation barrier; they are part of the encapsulation barrier.

                                                                            FAQ 19.05 What is a friend function?

                                                                            A friend function is a nonmember function that has been granted access to a class's non-public: members. This improves an interface without breaking encapsulation.

                                                                            For example, the syntax most objects use for printing is cout << x, where x is the object being printed and cout is the output stream (ostream) on which the object is being printed. This printing service is provided by operator<<, which needs to be a friend function of the class of x rather than a member function of the class of x, because the ostream needs to be on the left side of the << operator and the object being printed on the right side. In general, binary operators can be member functions only if the member function is attached to the left hand argument of the operator.

                                                                             #include <iostream>
                                                                             #include <cstdlib>
                                                                             using namespace std;
                                                                             
                                                                             class MyString {
                                                                             public:
                                                                               MyString(const char* s="")  throw();
                                                                              ~MyString()                  throw();
                                                                               MyString(const MyString& s) throw();
                                                                               MyString& operator= (const MyString& s) throw();
                                                                               friend ostream& operator<< (ostream& o, const MyString& s) throw();
                                                                             protected:
                                                                               char* s_;
                                                                             };
                                                                             
                                                                             MyString::MyString(const char* s) throw()
                                                                             : s_(strdup(s)) { }
                                                                             
                                                                             MyString::~MyString() throw()
                                                                             { free(s_); }
                                                                             
                                                                             ostream& operator<< (ostream& o, const MyString& s) throw()
                                                                             { return o << s.s_; }
                                                                             
                                                                             int main()
                                                                             {
                                                                               MyString s = "fred";
                                                                               cout << s << "\n";
                                                                             }
                                                                             

                                                                              FAQ 19.06 When should a function be implemented as a friend function rather than a member function?

                                                                              Whenever it improves readability of user code.

                                                                              Generally, member functions are used rather than friend functions. However if a friend function would make the code that uses the class more readable, a friend function should be used.

                                                                              It is important that the decision be based not on the readability of the code within the class but rather on the readability of the code that uses the class. There isn't that much difference between the internal implementation details within a friend function and those within a member function. But even more important is the economics of the situation. It's more important to focus on the many users of the class than it is to worry about the class itself.

                                                                              The point of friend functions is that they allow the syntax for the user code to be intuitive while still maintaining the class's encapsulation barrier. This can lead to easier-to-use classes, which reduces education costs and improves the quality of the user codeintuitive interfaces are abused less often.

                                                                                FAQ 19.07 What are some guidelines to make sure friend functions are used properly?

                                                                                Friend functions should be part of the class's public: interface, and their code should be owned by the class's owner.

                                                                                Guideline 1: Friend functions should make the user's code easier to understand. Look at some sample syntax of how a user would use the class with the friend function, and compare it with the moral equivalent of the sample syntax if the friend function were changed into a member function. The friend function version should be used if and only if it results in more intuitive user code (see FAQ 19.06).

                                                                                Guideline 2: Friend functions should be used only for operations that are part of the public: interface of a class. They should not be used every time someone wants to do something tricky with the class. If a user has a function that needs to access the innards of your class (for example, because the class's current public: interface isn't powerful enough), fix the problem (the interface) rather than patching the symptoms. Don't grant friendship to everyone.

                                                                                Guideline 3: A friend function or class should be under the political and technical authority of the same team that owns the class itself. Granting friendship status to a function or class under the political authority of a team other than the one implementing the class results in a scheduling headachechanges that involve coordinating multiple participants who may not always be in a position to handle the requested modifications in a timely manner is a nightmare.

                                                                                  FAQ 19.08 What does it mean that friendship isn't transitive?

                                                                                  A friend of a friend isn't (necessarily) a friend.

                                                                                  Friendship is personal; it is explicitly granted to a particular, named individual. All friends of a class are declared explicitly in the body of the class. This clearly identifies the entities that need to be updated when the private: part of a class is changed.

                                                                                  In the following code, operator<< is a friend of BinaryTree, which is a friend of BinaryTreeNode, but this does not make operator<< a friend of BinaryTreeNode.

                                                                                   #include <iostream>
                                                                                   using namespace std;
                                                                                   
                                                                                   class BinaryTreeNode;                                <-- 1
                                                                                   
                                                                                   class BinaryTree {
                                                                                   public:
                                                                                     friend ostream& operator<< (ostream&, const BinaryTree&) throw();
                                                                                                                                        <-- 2
                                                                                   protected:
                                                                                     BinaryTreeNode* root_;
                                                                                   };
                                                                                   
                                                                                   class BinaryTreeNode {
                                                                                   public:
                                                                                     // Public interface for BinaryTreeNode goes here
                                                                                   private:
                                                                                     friend BinaryTree;                                 <-- 3
                                                                                     BinaryTreeNode* left_;
                                                                                     BinaryTreeNode* right_;
                                                                                   };
                                                                                   
                                                                                   ostream& operator<< (ostream& ostr, const BinaryTree& bt) throw()
                                                                                   {
                                                                                     // This code can access bt.root_ (it's a friend of BinaryTree),
                                                                                     // but not bt.root_->left_ (it's not a friend of BinaryTreeNode).
                                                                                     return ostr;
                                                                                   }
                                                                                   

                                                                                  (1) Predeclare class BinaryTreeNode

                                                                                  (2) operator<< can access BinaryTree

                                                                                  (3) BinaryTree can access BinaryTreeNode

                                                                                  If operator<< needs to access BinaryTreeNode::left_ or BinaryTreeNode::right_, it must be made a friend of BinaryTreeNode as well:

                                                                                   class BinaryTreeNode {
                                                                                   public:
                                                                                     // Public interface for BinaryTreeNode goes here
                                                                                   private:
                                                                                     friend BinaryTree;                                 <-- 1
                                                                                     friend ostream& operator<< (ostream&, const BinaryTree&) throw();
                                                                                                                                        <-- 2
                                                                                     BinaryTreeNode* left_;
                                                                                     BinaryTreeNode* right_;
                                                                                   };
                                                                                   

                                                                                  (1) BinaryTree can access BinaryTreeNode

                                                                                  (2) operator<< can access BinaryTreeNode

                                                                                  Note that the compiler doesn't care where a friend declaration appears within a class, so the placement is normally done to make the code easily readable by other programmers (see FAQ 19.12). In the example, normal users might be somewhat confused by the friendship relationship between BinaryTreeNode and operator<<, so it has been moved out of the public: section (the public: section is where normal users look to find out how to use a class).

                                                                                    FAQ 19.09 What does it mean that friendship isn't inherited?

                                                                                    Just because someone trusts you does not automatically mean they will trust your children.

                                                                                    Suppose class Fred grants friendship privileges to another class Base and class Derived is derived from class Base. Derived does not automatically have friendship privileges to access the innards of Fred just because its base class is a friend of Fred. This rule improves encapsulation. Without this rule, anyone could automatically gain friendship (and access to internals) by deriving from a known friend.

                                                                                     class Base;
                                                                                     
                                                                                     class Fred {
                                                                                       friend Base;
                                                                                     };
                                                                                     
                                                                                     class Base {
                                                                                       //Member functions of Base are friends of Fred
                                                                                     };
                                                                                     
                                                                                     class Derived : public Base {
                                                                                       //Member functions of Derived are not friends of Fred
                                                                                     };
                                                                                     

                                                                                    In the following example, an EggCarton is not supposed to have more than a dozen eggs (numEggs_ <= 12). Class EggCartonFiller is trusted not to violate the semantics of an EggCarton, so EggCarton makes EggCartonFiller a friend. This friendship allows EggCartonFiller::addAnEgg() to access EggCarton::numEggs_.

                                                                                     class EggCartonFiller;   // Tell the compiler that
                                                                                                              //     "EggCartonFiller" is a class
                                                                                     
                                                                                     class EggCarton {
                                                                                     public:
                                                                                       EggCarton() throw();   // Creates an empty carton
                                                                                     private:
                                                                                       friend EggCartonFiller;
                                                                                       int numEggs_;          // numEggs_ can't exceed a dozen
                                                                                     };
                                                                                     
                                                                                     EggCarton::EggCarton()
                                                                                     : numEggs_(0) { }
                                                                                     
                                                                                     class EggCartonFiller {
                                                                                     public:
                                                                                       void addAnEgg(EggCarton& carton) throw();
                                                                                     };
                                                                                     
                                                                                     void EggCartonFiller::addAnEgg(EggCarton& carton) throw()
                                                                                     {
                                                                                       if (carton.numEggs_ < 12)
                                                                                         ++ carton.numEggs_;
                                                                                     }
                                                                                     

                                                                                    If friendship were inherited, anyone could create a class derived from EggCartonFiller and possibly violate the semantics of an EggCarton.

                                                                                     class SubversiveFiller : public EggCartonFiller {
                                                                                     public:
                                                                                       void violateEncapsulation(EggCarton& carton) throw();
                                                                                     };
                                                                                     
                                                                                     void SubversiveFiller::violateEncapsulation(EggCarton& carton) throw()
                                                                                     {
                                                                                       #ifdef GENERATE_ERROR
                                                                                         carton.numEggs_ = 13;                            <-- 1
                                                                                       #endif
                                                                                     }
                                                                                     

                                                                                    (1) Compile-time error: Can't access carton.numEggs_

                                                                                      FAQ 19.10 What does it mean that friends aren't virtual?

                                                                                      Friend functions don't bind dynamically. However there is a simple one-line idiom that enables the functionality of a virtual function (that is, dynamic binding) with the syntax of a friend function. This idiom is called the virtual friend function idiom.

                                                                                      The virtual friend function idiom provides the effect of friend functions that bind dynamically; it is used when the syntax of a friend function is desired but the operation must be dynamically bound.

                                                                                      Simply put, use a friend function that calls a protected: virtual member function. For example, suppose class Shape is an abstract base class (ABC), and a Shape is printed via cout << aShape, where aShape is a Shape&, which refers to an object of a derived class, such as Circle. To use the virtual friend function idiom, operator<< would be a friend of Shape and would call a protected: pure virtual member function such as print(ostream&) const.

                                                                                       #include <iostream>
                                                                                       using namespace std;
                                                                                       
                                                                                       class Shape {
                                                                                       public:
                                                                                         virtual ~Shape() throw();
                                                                                         friend ostream& operator<< (ostream& ostr, const Shape& s)
                                                                                                                     throw();
                                                                                       protected:
                                                                                         virtual void print(ostream& ostr) const throw() = 0;
                                                                                       };
                                                                                       
                                                                                       inline ostream& operator<< (ostream& ostr, const Shape& s)
                                                                                                                   throw()
                                                                                       {
                                                                                         s.print(ostr);                                     <-- 1
                                                                                         return ostr;
                                                                                       }
                                                                                       
                                                                                       Shape::~Shape() throw()
                                                                                       { }
                                                                                       

                                                                                      (1) The friend calls a protected: virtual

                                                                                      Because print() is virtual, the right implementation will always be invoked. Because print() is pure virtual, concrete derived classes are required to provide a definitionShape doesn't have enough knowledge about itself to print itself.

                                                                                       class Circle : public Shape {
                                                                                       public:
                                                                                         Circle() throw();
                                                                                       protected:
                                                                                         virtual void print(ostream& ostr) const throw();
                                                                                                                                            <-- 1
                                                                                         float radius_;
                                                                                       };
                                                                                       
                                                                                       Circle::Circle() throw()
                                                                                       : radius_(42) { }
                                                                                       
                                                                                       void Circle::print(ostream& ostr) const throw()
                                                                                       { ostr << "Circle of radius " << radius_; }
                                                                                       

                                                                                      (1) Derived classes override the member, not the friend

                                                                                      Because print() is protected:, users must use the official syntax provided by operator<< (this avoids cluttering the interface with two ways of doing the same thing).

                                                                                       void userCode(Shape& s)
                                                                                       { cout << s << '\n'; }                               <-- 1
                                                                                       
                                                                                       int main()
                                                                                       {
                                                                                         Circle c;
                                                                                         userCode(c);
                                                                                       }
                                                                                       

                                                                                      (1) Users use the friend, not the member

                                                                                      The output is

                                                                                       Circle of radius 42
                                                                                       

                                                                                      Note that there is only one operator<< for the entire Shape hierarchy. Derived classes provide a definition for print(ostream&) const, but they do not declare or define operator<<.

                                                                                        FAQ 19.11 What qualities suggest a friend function rather than a member function?

                                                                                        The three P's of friendship: position, promotion, or perception.

                                                                                        Position: Use a friend function when the object being operated on can't appear as the leftmost argument. For example, the syntax to print an object n is usually cout << n, where cout can be replaced by any ostream. Notice that n is not the leftmost argument and therefore operator<< cannot be a member of n's class. If operator<< needs access to n's internal state, it must be a friend of n's class.

                                                                                         #include <iostream>
                                                                                         using namespace std;
                                                                                         
                                                                                         class Fraction {
                                                                                         public:
                                                                                           Fraction(int num=0, int denom=1) throw();
                                                                                           friend ostream& operator<< (ostream& o, const Fraction& n)
                                                                                                                       throw();
                                                                                         protected:
                                                                                           int num_, denom_;
                                                                                         };
                                                                                         
                                                                                         Fraction::Fraction(int num, int denom) throw()
                                                                                         : num_   (num)
                                                                                         , denom_ (denom)
                                                                                         { }
                                                                                         
                                                                                         ostream& operator<< (ostream& o, const Fraction& n) throw()
                                                                                         { return o << n.num_ << '/' << n.denom_; }
                                                                                         
                                                                                         int main()
                                                                                         {
                                                                                           Fraction n = Fraction(3,8);    // "3/8"
                                                                                           cout << "n is " << n << '\n';
                                                                                         }
                                                                                         

                                                                                        Promotion: Use a friend function to allow promotion of the leftmost argument. For example, the Fraction class might want to support 5*n, where n is a Fraction object. This may require promoting the leftmost argument from an int to a Fraction, where this is implemented by passing a single int parameter to Fraction's constructorFraction(5). The operator* needs to be a friend because C++ never automatically promotes the this object in a member function invocation.

                                                                                         #include <iostream>
                                                                                         using namespace std;
                                                                                         
                                                                                         class Fraction {
                                                                                         public:
                                                                                           Fraction(int num=0, int denom=1) throw();
                                                                                           friend Fraction operator* (const Fraction& a,
                                                                                                                      const  Fraction& b) throw();
                                                                                         protected:
                                                                                           int num_, denom_;
                                                                                         };
                                                                                         
                                                                                         Fraction::Fraction(int num, int denom) throw()
                                                                                         : num_(num), denom_(denom) { }
                                                                                         
                                                                                         Fraction operator* (const Fraction& a, const Fraction& b) throw()
                                                                                         { return Fraction(a.num_*b.num_, a.denom_*b.denom_); }
                                                                                         
                                                                                         int main()
                                                                                         {
                                                                                           Fraction x = Fraction(3,8);        // "3/8"
                                                                                           Fraction y = 5*x;
                                                                                         }
                                                                                         

                                                                                        Perception: Use a friend function when it leads to a user syntax that is more intuitive. For example, two possible syntaxes for computing the square of a fraction n are n.square() and square(n) (for example, 1/2 squared is 1/4). If the operation is constructive (if n is unchanged), square(n) may be preferred because n.square() might be incorrectly perceived as squaring n itself.

                                                                                         #include <iostream>
                                                                                         using namespace std;
                                                                                         
                                                                                         class Fraction {
                                                                                         public:
                                                                                           Fraction(int num=0, int denom=1) throw();
                                                                                           friend Fraction square(const Fraction& n) throw();
                                                                                         protected:
                                                                                           int num_, denom_;
                                                                                         };
                                                                                         
                                                                                         Fraction::Fraction(int num, int denom) throw()
                                                                                         : num_(num), denom_(denom) { }
                                                                                         
                                                                                         Fraction square(const Fraction& n) throw()
                                                                                         { return Fraction(n.num_*n.num_, n.denom_*n.denom_); }
                                                                                         
                                                                                         int main()
                                                                                         {
                                                                                           Fraction x = Fraction(3,8);        // "3/8"
                                                                                           Fraction y = square(x);
                                                                                         }
                                                                                         

                                                                                        Of the three P's for choosing between friend functions and member functions, perception is the most subjective. In many cases involving perception, a static member function such as Fraction::square(n) is better than a friend function.

                                                                                          FAQ 19.12 Should friend functions be declared in the private:, protected:, or public: section of a class?

                                                                                          For documentation purposes, they should be declared in the public: section of a class. The compiler ignores the access level (private:, protected:, or public:) where friend functions are declared. However, for documentation purposes, they should normally be declared in the public: part of the class since friend functions are inherently public: (most friend functions are non-member functions and are therefore conceptually declared outside the class).

                                                                                          For an exception to this guideline, see FAQ 19.08.

                                                                                            FAQ 19.13 What is a private class?

                                                                                            A private class is a class created only for implementation purposes and is hidden from normal users. Typically, all its constructors (and often all its other members as well) are private: and it declares another class as its friend. Because the private class lacks public: constructors or member functions, only the designated friends and other instances of the private class can create or use instances of the private class.

                                                                                            For example, the Node class associated with a linked list class might be so specialized that no other class would benefit from reusing it. In this case the Node class can be a private class and can declare the linked list class as a friend. In the following code, class Node is nested inside class List. Although not strictly part of the private class concept, this technique has the further benefit of reducing the number of names in the outer namespace (nesting Node inside List removes the name Node from the namespace that List is in).

                                                                                             #include <new>
                                                                                             #include <cstdlib>
                                                                                             using namespace std;
                                                                                             
                                                                                             class List {
                                                                                             public:
                                                                                               List()                          throw();
                                                                                               List(const List& a)             throw(bad_alloc);
                                                                                              ~List()                          throw();
                                                                                               List& operator= (const List& a) throw(bad_alloc);
                                                                                               void prepend(int e)             throw(bad_alloc);
                                                                                               bool isEmpty() const            throw();
                                                                                               void clear()                    throw();
                                                                                             private:
                                                                                               class Node;                                        <-- 1
                                                                                               Node* first_;
                                                                                             };
                                                                                             
                                                                                             class List::Node {
                                                                                             private:                                             <-- 2
                                                                                               friend List;                                       <-- 3
                                                                                               Node(int e, Node* next=NULL) throw();
                                                                                               Node* next_;
                                                                                               int   elem_;
                                                                                             };
                                                                                             
                                                                                             List::Node::Node(int e, Node* next) throw() : next_(next), elem_(e) { }
                                                                                             List::List()               throw()          : first_(NULL) { }
                                                                                             List::List(const List& a)  throw(bad_alloc) : first_(NULL) { *this = a; }
                                                                                             List::~List()              throw()          { clear(); }
                                                                                             void List::prepend(int e)  throw(bad_alloc) { first_ =
                                                                                                                                           new Node(e, first_); }
                                                                                             bool List::isEmpty() const throw()          { return first_ == NULL; }
                                                                                             
                                                                                             int main()
                                                                                             {
                                                                                               List a;
                                                                                               a.prepend(4);
                                                                                               a.prepend(3);
                                                                                               a.prepend(2);
                                                                                               List b;
                                                                                               b = a;
                                                                                             }
                                                                                             

                                                                                            (1) The private class is called List::Node

                                                                                            (2) List::Node has no public: members

                                                                                            (3) List::Node declares List as its friend

                                                                                              FAQ 19.14 How are objects of a class printed?

                                                                                              Objects of a class are normally printed via a friend function called operator<<. Here is an example of such a friend function.

                                                                                               #include <iostream>
                                                                                               using namespace std;
                                                                                               
                                                                                               class Fred {
                                                                                               public:
                                                                                                 friend ostream& operator<< (ostream& ostr, const Fred& x) throw();
                                                                                               protected:
                                                                                                 int i_;
                                                                                               };
                                                                                               
                                                                                               ostream& operator<< (ostream& ostr, const Fred& x) throw()
                                                                                               {
                                                                                                 ostr << x.i_;
                                                                                                 return ostr;
                                                                                               }
                                                                                               

                                                                                              The function operator<< is a friend rather than a member, so the Fred parameter appears on the right side of the <<.

                                                                                                FAQ 19.15 How do objects of a class receive stream input?

                                                                                                Objects of a class normally receive stream input via a friend function called operator>>. Here is an example of such a friend function.

                                                                                                 #include <iostream>
                                                                                                 using namespace std;
                                                                                                 
                                                                                                 class Fred {
                                                                                                 public:
                                                                                                   friend istream& operator>> (istream& istr, Fred& x) throw();
                                                                                                 protected:
                                                                                                   int i_;
                                                                                                 };
                                                                                                 
                                                                                                 istream& operator>> (istream& istr, Fred& x) throw()
                                                                                                 {
                                                                                                   istr >> x.i_;
                                                                                                   return istr;
                                                                                                 }
                                                                                                 

                                                                                                The Fred argument of operator>> is passed by reference (as opposed to const reference). This allows operator>> to change the caller's Fred, which is, of course, the whole point of stream input.

                                                                                                  FAQ 20.01 What is the purpose of a constructor?

                                                                                                  The constructor turns a pile of incoherent, arbitrary bits into a living object. It initializes the object's internal data members, but it may also allocate resources (memory, files, semaphores, sockets, and so on). The word "ctor" is shorthand for the word "constructor."

                                                                                                  The constructors for class X are member functions named X. Here is an example.

                                                                                                   class Battery {
                                                                                                   public:
                                                                                                     Battery(int initialCharge) throw();
                                                                                                     void drain() throw();
                                                                                                   protected:
                                                                                                     int charge_;
                                                                                                   };
                                                                                                   
                                                                                                   Battery::Battery(int initialCharge) throw()
                                                                                                   : charge_(initialCharge) { }
                                                                                                   
                                                                                                   void Battery::drain() throw()
                                                                                                   {
                                                                                                     charge_ -= 5;
                                                                                                     if (charge_ < 0)
                                                                                                       charge_ = 0;
                                                                                                   }
                                                                                                   
                                                                                                   int main()
                                                                                                   {
                                                                                                     Battery yourDiscountBattery(20); //A Battery object
                                                                                                     Battery myNameBrandBattery(30);  //Another Battery object
                                                                                                   }
                                                                                                   

                                                                                                  There can be more than one constructor for a class. Each constructor has the same name, so the compiler uses their signatures to uniquely identify them.

                                                                                                    FAQ 20.02 What is C++'s constructor discipline?

                                                                                                    A constructor is automatically called at the moment an object is created.

                                                                                                    Descartes said, "I think, therefore I am." The C++ variation of Descartes' statement is "I am, therefore I can think." In other words, every object that exists ("I am") has been initialized by one of the class's constructors ("I can think"). Except for pathological cases, by the time an object is accessible, it has already been initialized by its constructor.

                                                                                                    The developers of a class provide a set of constructors that define how objects of that class can be initialized. When users create objects of that class, they must provide arguments that match the signature of one of the class's constructors. Constructors enhance encapsulation since they force users to create objects in one of the officially supported ways. Users cannot initialize an object's state directly, because this might place the object in an incoherent or illegal state.

                                                                                                    In the example from the previous FAQ, the constructor for class Battery is the member function Battery::Battery(int initialCharge). This constructor initializes the protected: data member charge_ to the value passed as the parameter to the constructor. The constructor is called twice in main(), once when yourDiscountBattery is created and once when myNameBrandBattery is created.

                                                                                                      FAQ 20.03 What is the purpose of a destructor?

                                                                                                      The destructor is the last member function ever called for an object. The destructor's typical purpose is to release resources that the object is holding. The word "dtor" is shorthand for the word "destructor."

                                                                                                      A class can have at most one destructor. For a class named X, the destructor is a member function named ~X().

                                                                                                      Just as a constructor turns a pile of incoherent, arbitrary bits into a living object, a destructor turns a living object into a pile of incoherent, arbitrary bits. A destructor blows an object to bits.

                                                                                                        FAQ 20.04 What is C++'s destructor discipline?

                                                                                                        If the class of an object has a destructor, C++ guarantees that the destructor is called when the object dies. A local (auto) object dies at the close of the block ({...}) in which it was created (that is, when it is conceptually popped from the runtime stack). An unbound temporary object dies at the end of the outermost expression in which the temporary was generated (often this is at the next ;). A member object dies when its containing object dies. An array element dies when its array dies. An object allocated via new (dynamically) dies when the object is deleted. With a static object, death occurs sometime after main() finishes.

                                                                                                        Everything but dynamically allocated objects (that is, local objects, temporary objects, member objects, static objects, and array elements) are destructed in the reverse order of construction: first constructed is last destructed. In the following example, b's destructor is executed first, then a's destructor.

                                                                                                         void sample() throw()
                                                                                                         {
                                                                                                           Fred a;
                                                                                                           Fred b;
                                                                                                           // ...
                                                                                                         }
                                                                                                         

                                                                                                        Warning: Do not use longjmp with C++ because it subverts the guarantee that destructors will be called.

                                                                                                          FAQ 20.05 What happens when a destructor is executed?

                                                                                                          The destructor automatically calls the destructors for all member objects and all immediate nonvirtual base classes. First, the destructor's body ({...}) is executed, then the destructors for member objects are called in the reverse order that the member objects appear in the class body, then the destructors for immediate base classes are called (in the reverse order they appear in the class declaration). Virtual base classes are specialtheir destructors are called at the end of the most derived class's destructor (only).

                                                                                                          For example, suppose lock(int) and unlock(int) provide the primitives to manage mutual exclusion. The C++ interface to these primitives would be a Lock class, whose constructor calls lock(int) and whose destructor calls unlock(int).

                                                                                                           #include <iostream>
                                                                                                           using namespace std;
                                                                                                           
                                                                                                           void lock(int i) throw()
                                                                                                           {
                                                                                                             cout << "pretend we acquire lock #" << i << '\n';
                                                                                                             //In reality, this would manipulate semaphore #i
                                                                                                             // (or use some other mutual exclusion primitive)
                                                                                                           }
                                                                                                           
                                                                                                           void unlock(int i) throw()
                                                                                                           {
                                                                                                             cout << "pretend we release lock #" << i << '\n';
                                                                                                             //In reality, this would manipulate semaphore #i
                                                                                                             // (or use some other mutual exclusion primitive)
                                                                                                           }
                                                                                                           
                                                                                                           class Lock {
                                                                                                           public:
                                                                                                             Lock(int lockNum) throw();
                                                                                                            ~Lock()            throw();
                                                                                                           protected:
                                                                                                             int lockNum_;
                                                                                                           private:
                                                                                                             // These are never defined (copying a Lock is senseless)
                                                                                                             Lock(const Lock&);
                                                                                                             Lock& operator= (const Lock&);
                                                                                                           };
                                                                                                           
                                                                                                           inline Lock::Lock(int lockNum) throw()
                                                                                                           : lockNum_(lockNum) { lock(lockNum_); }
                                                                                                           
                                                                                                           inline Lock::~Lock() throw()
                                                                                                           { unlock(lockNum_); }
                                                                                                           
                                                                                                           void multiThreadedFunction() throw()
                                                                                                           {
                                                                                                             cout << "no mutual exclusion here\n";
                                                                                                             {
                                                                                                               Lock lock(42);   //Pretend this is Critical section (42)
                                                                                                               cout << "lock provides mutual exclusion here\n";
                                                                                                             }
                                                                                                           
                                                                                                             cout << "no mutual exclusion here\n";
                                                                                                           }
                                                                                                           

                                                                                                          A whimsical developer might rename class Lock to class Critical, so that if the user code declared an object named section, Lock lock(42) would then become Critical section(42). But regardless of the names of the class and its object, the important point is that there is no need to explicitly unlock the semaphore. This eliminates a potential (and dangerous!) source of errors.

                                                                                                            FAQ 20.06 What is the purpose of a copy constructor?

                                                                                                            It initializes an object by copying the state from another object of the same class.

                                                                                                            Whenever an object is copied, another object (the copy) is created, so a constructor is called (see FAQ 20.02). This constructor is called the copy constructor. If the class of the object being copied is X, the copy constructor's signature is usually X::X(const X&).

                                                                                                            One way to pronounce X(const X&) is X-X-ref (pretend the const is silent). The first X refers to the name of the member function, and the X-ref refers to the type of the parameter. Some people prefer the shorthand X-X-ref to the phrase copy constructor.

                                                                                                            In the following example, the copy constructor is MyString::MyString(const MyString&). Notice how it initializes the new MyString object to be a copy of the source MyString object.

                                                                                                             #include <new>
                                                                                                             #include <cstring>
                                                                                                             using namespace std;
                                                                                                             class MyString {
                                                                                                             public:
                                                                                                               MyString(const char* s) throw(bad_alloc);          <-- 1
                                                                                                               MyString(const MyString& source) throw(bad_alloc);   <-- 2
                                                                                                               ~MyString() throw();                               <-- 3
                                                                                                               MyString& operator= (const MyString& s)
                                                                                                                                    throw(bad_alloc);             <-- 4
                                                                                                             protected:
                                                                                                               unsigned len_;   // ORDER DEPENDENCY; see FAQ 22.10
                                                                                                               char*    data_;
                                                                                                             };
                                                                                                             
                                                                                                             MyString::MyString(const char* s) throw(bad_alloc)
                                                                                                             : len_(strlen(s))
                                                                                                             , data_(new char[len_+1])
                                                                                                             { memcpy(data_, s, len_+1); }
                                                                                                             
                                                                                                             MyString::MyString(const MyString& source) throw(bad_alloc)
                                                                                                             : len_(source.len_)
                                                                                                             , data_(new char[source.len_+1])
                                                                                                             { memcpy(data_, source.data_, len_+1); }
                                                                                                             
                                                                                                             MyString::~MyString() throw()
                                                                                                             { delete[] data_; }
                                                                                                             
                                                                                                             int main()
                                                                                                             {
                                                                                                               MyString a = "xyzzy"; //Calls MyString::MyString(const char*)
                                                                                                               MyString b = a;       //Calls MyString::MyString(const MyString&)
                                                                                                             }
                                                                                                             

                                                                                                            (1) ctor to promote a const char*

                                                                                                            (2) Copy constructor

                                                                                                            (3) Destructor

                                                                                                            (4) Assignmentoperator

                                                                                                              FAQ 20.07 When is a copy constructor invoked?

                                                                                                              When an object is passed by value, returned by value, or explicitly copied. Here is an example showing all three situations.

                                                                                                               #include <iostream>
                                                                                                               using namespace std;
                                                                                                               
                                                                                                               class X {
                                                                                                               public:
                                                                                                                 X()         throw();
                                                                                                                 X(const X&) throw();
                                                                                                               };
                                                                                                               
                                                                                                               X::X()         throw() { cout << "default constructor\n"; }
                                                                                                               X::X(const X&) throw() { cout << "copy constructor\n";    }
                                                                                                               
                                                                                                               X userCode(X b) throw()                              <-- 1
                                                                                                               {
                                                                                                                 X c = b;                                           <-- 2
                                                                                                                 return c;
                                                                                                               }                                                    <-- 3
                                                                                                               
                                                                                                               int main()
                                                                                                               {
                                                                                                                 X a;
                                                                                                                 cout << "calling userCode()\n";
                                                                                                                 X d = userCode(a);
                                                                                                                 cout << "back in main()\n";
                                                                                                               }
                                                                                                               

                                                                                                              (1) Pass by value: Copy main()'s a to b

                                                                                                              (2) Explicit copy: Copy from b to c

                                                                                                              (3) Return by value: Copy from c to main()'s d

                                                                                                              The (annotated) output of this program is

                                                                                                               default constructor                                  <-- 1
                                                                                                               calling userCode()
                                                                                                               copy constructor                                     <-- 2
                                                                                                               copy constructor                                     <-- 3
                                                                                                               copy constructor                                     <-- 4
                                                                                                               back in main()
                                                                                                               

                                                                                                              (1) main()'s a

                                                                                                              (2) userCode()'s b

                                                                                                              (3) userCode()'s c

                                                                                                              (4) main()'s d

                                                                                                              Note that pass-by-value calls the copy constructor if the caller supplies another object of the same class. Supplying something else may invoke a different constructor. Similar comments apply to return-by-value.

                                                                                                                FAQ 20.08 What is the "default constructor"?

                                                                                                                It's the constructor that can be called with no arguments.

                                                                                                                For a class Fred, the default constructor is often simply Fred::Fred(), since certainly that can be called (and in this case, must be called) with no arguments. However it is possible (and even likely) for a default constructor to take arguments, provided they are given default values.

                                                                                                                 class Fred {
                                                                                                                 public:
                                                                                                                   Fred(int i=3, int j=5) throw();                    <-- 1
                                                                                                                   // ...
                                                                                                                 };
                                                                                                                 

                                                                                                                (1) Default constructor: Can be called with no args

                                                                                                                In either case, the default constructor is called when an object is created without any initializers. For example, object x is created by calling Fred::Fred(int,int) with defaulted arguments (3,5).

                                                                                                                 void sample() throw()
                                                                                                                 {
                                                                                                                   Fred x;                                            <-- 1
                                                                                                                   Fred y(3,5);                                       <-- 2
                                                                                                                   // ...
                                                                                                                 }
                                                                                                                 

                                                                                                                (1) Calls the default constructor

                                                                                                                (2) Same effect as with object x

                                                                                                                Similarly the default constructor (in this case, including the defaulted parameters) is called for each element of an array. For example, each element of array a and vector b (see FAQ 28.13) is initialized by calling Fred::Fred(int,int) and passing the defaulted arguments (3,5). However, vectors are more powerful than arrays, since vectors can use something other than the default constructor when initializing the array elements (an especially important feature for those classes that don't have a default constructor). For example, the 10 elements of vector c are initialized with Fred(7,9) rather than the default choice of Fred(3,5):

                                                                                                                 #include <new>
                                                                                                                 #include <vector>
                                                                                                                 using namespace std;
                                                                                                                 
                                                                                                                 void sample2() throw(bad_alloc)
                                                                                                                 {
                                                                                                                   Fred a[10];                                        <-- 1
                                                                                                                   vector<Fred> b(10);                                <-- 2
                                                                                                                   vector<Fred> c(10, Fred(7,9));                     <-- 3
                                                                                                                 }
                                                                                                                 

                                                                                                                (1) Arrays use the contained object's default ctor

                                                                                                                (2) vector can use the contained object's default ctor

                                                                                                                (3) vector doesn't have to use the default ctor

                                                                                                                Note that adding an empty pair of parentheses after a declaration is not the same as calling the default constructor. In the following example, x is an object of class Fred, but y is declared to be a function that returns a Fred by value; y is not a Fred object.

                                                                                                                 void sample3() throw()
                                                                                                                 {
                                                                                                                   Fred x;                                            <-- 1
                                                                                                                   Fred y();                                          <-- 2
                                                                                                                   // ...
                                                                                                                 }
                                                                                                                 

                                                                                                                (1) Creates an object

                                                                                                                (2) Declares a function; does not create a Fred object

                                                                                                                  FAQ 20.09 Should one constructor call another constructor as a primitive?

                                                                                                                  No. Even though it is possible, it won't do what the programmer wants.

                                                                                                                  Dragons be here: if a constructor of class Fred calls another constructor of class Fred, the compiler actually initializes a temporary local object of class Fred. It does not use the call to initialize the this object.

                                                                                                                  Both constructors can be combined by using a default parameter or their common code can be shared in a private: init() member function, but one constructor should not directly call another constructor. Here is an example of an init() function.

                                                                                                                   class Fred {
                                                                                                                   public:
                                                                                                                     Fred(int x)   throw();
                                                                                                                     Fred(float y) throw();
                                                                                                                     Fred(char* z) throw();
                                                                                                                     // ...
                                                                                                                   private:
                                                                                                                     void init(int x, float y, char* z) throw();
                                                                                                                     // ...
                                                                                                                   };
                                                                                                                   
                                                                                                                   inline Fred::Fred(int x)   throw() { init(x, 0.3, NULL); }
                                                                                                                   inline Fred::Fred(float y) throw() { init(-1, y, "foo"); }
                                                                                                                   inline Fred::Fred(char* z) throw() { init(-2, 5.2, z);   }
                                                                                                                   
                                                                                                                   void Fred::init(int x, float y, char* z) throw()
                                                                                                                   {
                                                                                                                     // ...
                                                                                                                   }
                                                                                                                   

                                                                                                                    FAQ 20.10 Does the destructor for a derived class need to explicitly call the destructor of its base class?

                                                                                                                    No. The runtime system calls the destructor for the base class after the destructor for the derived class finishes executing.

                                                                                                                    Never call a destructor explicitly. The only exception to this is the fairly esoteric case of destructing an object that was initialized by the placement new operator (see FAQ 12.14).

                                                                                                                      FAQ 20.11 How can a local object be destructed before the end of its function?

                                                                                                                      The most important message is to not explicitly call a local object's destructor. The language guarantees that the object's destructor will be called again at the close of the scope in which the local was created (see FAQ 20.04). So if the destructor is called explicitly, it will be called again at the close of the scope, which can have disastrous results. Bang; you're dead.

                                                                                                                      The easiest way to make sure a local object is destructed before the end of its scope is to insert a pair of braces (that is, a new scope) so that the object will be destructed at the right time. For example, suppose the (desirable) side effect of destructing a local File object is to close the File. Now suppose a local File object needs to be closed before the end of the scope (i.e., the }) of its function. In this case simply add an extra pair of braces to scope the lifetime of the object:

                                                                                                                       void sample()
                                                                                                                       {
                                                                                                                         // ...
                                                                                                                       
                                                                                                                         {                                                  <-- 1
                                                                                                                           File x;
                                                                                                                           // ...                                           <-- 2
                                                                                                                           // ...
                                                                                                                         }                                                  <-- 3
                                                                                                                       
                                                                                                                         // ...                                             <-- 4
                                                                                                                       }
                                                                                                                       

                                                                                                                      (1) Added to limit the scope of File x

                                                                                                                      (2) This code will execute while File x is still open

                                                                                                                      (3) File x will close here

                                                                                                                      (4) This code will execute after File x is closed

                                                                                                                      In cases where this extra pair of braces cannot be added, add an extra member function that causes the destructor's desirable side effects to occur. This member function should mark the object so that the destructor, which will inevitably be called at the close of the object's scope, will be able to tell if the side effects have already happened. For example, a close() member function could be added to the File class, and that member function could be called where the file should be closed:

                                                                                                                       void sample()
                                                                                                                       {
                                                                                                                         // ...
                                                                                                                       
                                                                                                                         File x;
                                                                                                                         // ...                                             <-- 1
                                                                                                                       
                                                                                                                         x.close();                                         <-- 2
                                                                                                                       
                                                                                                                         // ...                                             <-- 3
                                                                                                                       }
                                                                                                                       

                                                                                                                      (1) This code will execute while File x is still open

                                                                                                                      (2) File x will close here

                                                                                                                      (3) This code will execute after File x is closed

                                                                                                                      The close() member function could mark the object so the destructor knows not to reclose the file, such as setting the underlying file handle to some invalid value such as -1. To avoid duplication of code in the destructor and the close() member function, the destructor could simply call the close() member function, and the close() member function could check to see if the file handle is in the "already closed" state.

                                                                                                                       #include <iostream>
                                                                                                                       #include <stdexcept>
                                                                                                                       using namespace std;
                                                                                                                       
                                                                                                                       int openFile(const char* name) throw()
                                                                                                                       {
                                                                                                                         // Normally this code would actually open the named file.
                                                                                                                         // For this example, we pretend everything is handle 42.
                                                                                                                         int handle = 42;
                                                                                                                         cout << "opening " << name << " as #" << handle << '\n';
                                                                                                                         return handle;
                                                                                                                       }
                                                                                                                       
                                                                                                                       void closeFile(int handle) throw()
                                                                                                                       {
                                                                                                                         // Normally this code would actually close the file
                                                                                                                         cout << "closing file #" << handle << '\n';
                                                                                                                       }
                                                                                                                       
                                                                                                                       class File {
                                                                                                                       public:
                                                                                                                         File(const char* name) throw();
                                                                                                                         ~File() throw();
                                                                                                                         void close() throw();
                                                                                                                       protected:
                                                                                                                         enum { closed_ = -1 };                             <-- 1
                                                                                                                         int handle_;
                                                                                                                       private:
                                                                                                                         // These are never defined; copy semantics are ill defined
                                                                                                                         File(const File&);
                                                                                                                         File& operator= (const File&);
                                                                                                                       };
                                                                                                                       
                                                                                                                       inline void File::close()
                                                                                                                       {
                                                                                                                         if (handle_ != closed_) closeFile(handle_);
                                                                                                                         handle_ = closed_;
                                                                                                                       }
                                                                                                                       
                                                                                                                       inline File::File(const char* name) throw()
                                                                                                                       : handle_( openFile(name) ) { }
                                                                                                                       
                                                                                                                       inline File::~File() throw()
                                                                                                                       { close(); }
                                                                                                                       
                                                                                                                       void userCode(bool throwIt) throw(runtime_error)
                                                                                                                       {
                                                                                                                         File x("sample.txt");
                                                                                                                         cout << "after open, before throw-or-close\n";
                                                                                                                         if (throwIt)
                                                                                                                            throw runtime_error("note that the file still gets closed!");
                                                                                                                         x.close();
                                                                                                                         cout << "after close\n";
                                                                                                                       }
                                                                                                                       
                                                                                                                       int main()
                                                                                                                       {
                                                                                                                         cout << "====== without throwing an exception ======\n";
                                                                                                                         userCode(false);
                                                                                                                       
                                                                                                                         cout << "====== with throwing an exception =========\n";
                                                                                                                         try {
                                                                                                                           userCode(true);
                                                                                                                         }
                                                                                                                         catch (exception& e) {
                                                                                                                           cout << "exception caught; " << e.what() << '\n';
                                                                                                                         }
                                                                                                                       }
                                                                                                                       

                                                                                                                      (1) File::closed_ is a constant

                                                                                                                      The output of this program follows.

                                                                                                                       ====== without throwing an exception ======
                                                                                                                       opening sample.txt as #42
                                                                                                                       after open, before throw-or-close
                                                                                                                       closing file #42
                                                                                                                       after close
                                                                                                                       ====== with throwing an exception =========
                                                                                                                       opening sample.txt as #42
                                                                                                                       after open, before throw-or-close
                                                                                                                       closing file #42
                                                                                                                       exception caught; note that the file still gets closed!
                                                                                                                       

                                                                                                                      It is important to note that closeFile() is called exactly once per open file, whether or not an exception is thrown or the x.close() instruction is reached. Even if x.close() were explicitly called twice, the underlying file would only be closed once.

                                                                                                                        FAQ 20.12 What is a good way to provide intuitive, multiple constructors for a class?

                                                                                                                        Use the named constructor idiom.

                                                                                                                        Constructors always have the same name as the class, so the only way to differentiate between the various constructors of a class is by the parameter list. If there are numerous overloaded constructors, the differences can become somewhat subtle and subject to error.

                                                                                                                        With the named constructor idiom, all the constructors are declared as private: or protected:, and the class provides public: static member functions that return an object. These static member functions are the so-called named constructors. In general there is one such static member function for each different way to construct an object.

                                                                                                                        For example, consider a Point class that represents a position on a plane. There are two different constructors corresponding to the two common ways to specify a point on a plane: rectangular coordinates (X and Y) and polar coordinates (radius and angle). Unfortunately the parameters for these two coordinate systems are the same (two numbers), so the overloaded constructors would be ambiguous. For example, if someone created a point using Point(5.7, 1.2), the compiler wouldn't know which coordinate system (that is, which constructor) to use.

                                                                                                                        One way to solve this ambiguity is to use the named constructor idiom:

                                                                                                                         #include <cmath> // To get sin() and cos()
                                                                                                                         #include <iostream>
                                                                                                                         using namespace std;
                                                                                                                         
                                                                                                                         class Point {
                                                                                                                         public:
                                                                                                                           static Point rectangular(float x, float y) throw();
                                                                                                                           // Rectangular coordinates
                                                                                                                           static Point polar(float radius, float angle) throw();
                                                                                                                           // Polar coordinates
                                                                                                                           // These static members are the so-called named constructors
                                                                                                                           // ...
                                                                                                                         private:
                                                                                                                           Point(float x, float y) throw();  // Rectangular coordinates
                                                                                                                           float x_, y_;
                                                                                                                         };
                                                                                                                         
                                                                                                                         inline Point::Point(float x, float y) throw()
                                                                                                                         : x_(x), y_(y) { }
                                                                                                                         
                                                                                                                         inline Point Point::rectangular(float x, float y) throw()
                                                                                                                         { return Point(x, y); }
                                                                                                                         
                                                                                                                         inline Point Point::polar(float radius, float angle) throw()
                                                                                                                         { return Point(radius*cos(angle), radius*sin(angle)); }
                                                                                                                         

                                                                                                                        Now the users of Point have a clear and unambiguous syntax for creating Points in either coordinate system:

                                                                                                                         int main()
                                                                                                                         {
                                                                                                                           Point a = Point::rectangular(5.7, 1.2); // Obviously rectangular
                                                                                                                           Point b = Point::polar(5.7, 1.2);       // Obviously polar
                                                                                                                         }
                                                                                                                         

                                                                                                                        Note that the constructor(s) must be in the protected: section, not the private: section, if the class will have derived classes.

                                                                                                                        The Named Constructor Idiom can also be used to make sure objects are always created via new. In this case the public: static member functions (the named constructors) should allocate the object via new and should return a pointer to the allocated object.

                                                                                                                          FAQ 20.13 When the constructor of a base class calls a virtual function, why isn't the override called?

                                                                                                                          C++ is ensuring that member objects are initialized before they are used.

                                                                                                                          Objects of a derived class mature during construction. While the base class's constructor is executing, the object is merely a base class object. Later, when the derived class's constructor begins executing, the object matures into a derived class object. If a virtual function is invoked while the object is still immature, the immature version of the virtual function is called. It may sound confusing, but it's the only sensible way to do it without having across-the-board reference semantics.

                                                                                                                          For example, suppose class Derived overrides an inherited virtual function f(), and Base::Base() calls f(). Since the object is a Base during the execution of Base::Base(), Base::f() is invoked. If C++ allowed Base::Base() to call Derived::f(), Derived::f() might invoke member functions on a member object that had not yet been constructed!

                                                                                                                           #include <iostream>
                                                                                                                           using namespace std;
                                                                                                                           
                                                                                                                           class MemberObject {
                                                                                                                           public:
                                                                                                                             MemberObject()     throw();
                                                                                                                             void doSomething() throw();
                                                                                                                           };
                                                                                                                           
                                                                                                                           MemberObject::MemberObject()     throw()
                                                                                                                           { cout << "MemberObject ctor\n"; }
                                                                                                                           void MemberObject::doSomething() throw()
                                                                                                                           { cout << "MemberObject used\n"; }
                                                                                                                           
                                                                                                                           class Base {
                                                                                                                           public:
                                                                                                                             Base()           throw();
                                                                                                                             virtual void f() throw();
                                                                                                                           };
                                                                                                                           
                                                                                                                           Base::Base()   throw() { cout << "Base ctor\n"; f(); }
                                                                                                                           void Base::f() throw() { cout << "Base::f()\n"; }
                                                                                                                           
                                                                                                                           class Derived : public Base {
                                                                                                                           public:
                                                                                                                             Derived()        throw();
                                                                                                                             virtual void f() throw();
                                                                                                                           protected:
                                                                                                                             MemberObject m_;
                                                                                                                           };
                                                                                                                           
                                                                                                                           Derived::Derived() throw()
                                                                                                                           : Base(), m_() { cout << "Derived ctor\n"; }
                                                                                                                           void Derived::f()  throw()
                                                                                                                           { cout << "Derived::f()\n"; m_.doSomething(); }
                                                                                                                           
                                                                                                                           int main()
                                                                                                                           {
                                                                                                                             Derived d;
                                                                                                                             cout << "====\n";
                                                                                                                             d.f();
                                                                                                                           }
                                                                                                                           

                                                                                                                          The output of this program follows.

                                                                                                                           Base ctor
                                                                                                                           Base::f()
                                                                                                                           MemberObject ctor
                                                                                                                           Derived ctor
                                                                                                                           ====
                                                                                                                           Derived::f()
                                                                                                                           MemberObject used
                                                                                                                           

                                                                                                                          If C++ allowed Base::Base() to call Derived::f(), m_.fred() would be called before m_ was constructed.

                                                                                                                          C++ programmers need to be aware that Java does things quite differently, and therefore Java has a completely different problem. In Java, the derived class's override does get called when the base class's constructor invokes a method. This means that the derived class's override cannot assume that the derived class's constructor was run before the member function is called, so member variables that the derived class's constructor sets to some non-null state may in fact still be null when the override is called.

                                                                                                                          Neither the C++ approach nor the Java approach is a clear winner in terms of being intuitive and lacking surprises. So the most important point is to understand the differences and be prepared for a learning curve when moving between the two languages.

                                                                                                                            FAQ 20.14 When a base class destructor calls a virtual function, why isn't the override called?

                                                                                                                            C++ is helping ensure that member objects are not used after destruction.

                                                                                                                            Just as an object of a derived class matures into a derived class object during construction, it reverts back into a base class object during destruction.

                                                                                                                            Extending the example from the previous FAQ, if Base::~Base() calls f(), Base::f() is invoked (rather than Derived::f()) because the object has already reverted to a mere Base.

                                                                                                                            This is the right thing to do. If Base::~Base() could call Derived::f(), the destructed MemberObject would be used, leading to unpredictable results.

                                                                                                                            Many people don't think that this rule is intuitively obvious, either. Once again, it is an issue of reference semantics versus value semantics, and C++ was designed to maximize compatibility with C. And once again, Java does things differently, which causes its own set of problems.

                                                                                                                              FAQ 20.15 What is the purpose of placement new?

                                                                                                                              It's a way to pass parameters to the allocator rather than just to the constructor.

                                                                                                                              Allocating an object from the heap, such as new Fred(5,7), is a two-step process: first an appropriately sized and aligned block of uninitialized memory is allocated from the heap, then the constructor is called with the this pointer pointing to that block of memory. Parameters are often passed to the constructor (for example, the above example passes (5,7)), but occasionally parameters also must be passed to the allocation step. For example, if there was a special allocator that used a particular pool of memory, it might be necessary to pass a reference to that pool of memory to the allocation step, that is, to new itself: new(myPool) Fred(5,7).

                                                                                                                              Another common reason to pass a parameter for the allocation step is to pass a pointer to a particular preallocated region of memory. For example, if pointer p is a void* that points to a pile of memory that is at least sizeof(Fred) bytes long and is appropriately aligned, one could say new(p) Fred(5,7). This would construct a Fred object at the location pointed to by p (that is, it would pass p as the this pointer to Fred's constructor) and would ultimately return a Fred* that would point to the same location that void* p points to. For example,

                                                                                                                               #include <new>      // Must #include this to use placement new
                                                                                                                               using namespace std;
                                                                                                                               #include "Fred.hpp" // Declaration of class Fred
                                                                                                                               
                                                                                                                               void sample() throw()
                                                                                                                               {
                                                                                                                                 char memory[sizeof(Fred)];  // Line 1
                                                                                                                                 void* p = memory;           // Line 2
                                                                                                                                 Fred* f = new(p) Fred();    // Line 3 (be careful here!)
                                                                                                                                 // The pointers f and p will be equal
                                                                                                                                 // ...
                                                                                                                               }
                                                                                                                               

                                                                                                                              Line 1 creates an array of sizeof(Fred) bytes of memory, which is big enough to hold a Fred object. Line 2 creates a pointer p that points to the first byte of this memory (experienced C programmers will note that this step is unnecessary; it's there only to make the code more obvious). Line 3 essentially calls the constructor Fred::Fred(). The this pointer in the Fred constructor is equal to p. The returned pointer f is equal to p.

                                                                                                                              Passing a void* pointer with the placement new syntax should be used only when it is essential to place an object at a particular location. For example, when writing an operating system, the placement new syntax could be used to place a Clock object at a particular memory-mapped I/O timer device. Neither the compiler nor the runtime system make any attempt to check whether the passed pointer points to a region of memory that is big enough and is properly aligned for the object being created. For example, if Fred objects need to be aligned on a 4-byte boundary but the supplied pointer p isn't properly aligned, it could be a serious (and subtle) disaster. You have been warned.

                                                                                                                              Also, the programmer takes sole responsibility to deallocate the object when the placement new syntax is used. This is done by explicitly calling the destructor, which is one of the few times a destructor should be called explicitly:

                                                                                                                               void sample2() throw()
                                                                                                                               {
                                                                                                                                 char memory[sizeof(Fred)];
                                                                                                                                 void* p = memory;
                                                                                                                                 Fred* f = new(p) Fred();
                                                                                                                                 // ...
                                                                                                                                 f->~Fred();                                        <-- 1
                                                                                                                               }
                                                                                                                               

                                                                                                                              (1) Explicitly call the destructor for the placed object

                                                                                                                                Оставьте свой комментарий !

                                                                                                                                Ваше имя:
                                                                                                                                Комментарий:
                                                                                                                                Оба поля являются обязательными

                                                                                                                                 Автор  Комментарий к данной статье