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
 Linux Kernel 2.6...5164 
 Trees...935 
 Максвелл 3...861 
 Go Web ...814 
 William Gropp...795 
 Ethreal 3...779 
 Ethreal 4...766 
 Gary V.Vaughan-> Libtool...764 
 Rodriguez 6...755 
 Steve Pate 1...748 
 Clickhouse...748 
 Ext4 FS...748 
 Ethreal 1...736 
 Secure Programming for Li...720 
 C++ Patterns 3...711 
 Ulrich Drepper...693 
 Assembler...687 
 DevFS...655 
 Стивенс 9...644 
 MySQL & PosgreSQL...621 
 
  01.01.2024 : 3621733 посещений 

iakovlev.org

C++ FAQ

Код лежит тут

FAQ 11.01 Что такое ссылка?

Ссылка - это алиас , или альтернативное название для обьекта. Ссылки часто используются для передачи параметров (pass-by-reference; see FAQ 2.09). В следующем примере, функция swap() получает параметры по ссылке и возвращает их измененные в main.

 #include <iostream>
 using namespace std;
 
 void swap(int& x, int& y) throw()
 {
   int temp = x;
   x = y;
   y = temp;
 }
 
 int main()
 {
   int i = 5;
   int j = 7;
   cout << "before: i=" << i << ", j=" << j << '\n';
   swap(i, j);
   cout << "after:  i=" << i << ", j=" << j << '\n';
 }
 

Here x and y become aliases for main()'s i and j, respectively. The effect is similar to the C-style pass-by-pointer, but without the caller having to take the address of the parameters and without the callee having to dereference pointers. That is, it would be illegal to change swap(i, j) in main() to swap(&i, &j), and it would be illegal to change x = y in swap() to *x = *y.

    FAQ 11.02 Что означает "referent" ?

    "Referent" is a synonym for the object to which the reference refers; see FAQ 2.09. In the following example, j is the reference and i is the referent.

     int main()
     {
       int  i;
       int& j = i;
     }
     

      FAQ 11.03 Когда ссылка может быть приаттачена к обьекту ?

      A reference can be attached to its referent only at the moment the reference is initialized. Not only that, C++ requires every reference initialization to have a referent.

      The following example initializes j to be an alias for i, but the initialization of k is illegal because reference k is not attached to an object.

       int main()
       {
         int  i;
         int& j = i;
       
         #ifdef GENERATE_ERROR
           int& k;   //ERROR: references must be initialized
         #endif
       }
       

      When a function receives a parameter by reference, the reference is initialized by being attached (bound) to the actual argument provided by the caller.

        FAQ 11.04 Что происходит , когда ссылке присваивается значение ?

        The reference remains bound to the same referent, and the value of the referent is changed.

        Because a reference is an alias for its referent, anything done to the reference is actually done to the referent. In particular, a reference is an lvalue (an expression that can appear on the left side of an assignment operator) for the referent. Therefore, assigning to a reference changes the referent.

        Said another way, a reference is its referentnot a copy of the referent nor a pointer to the referent, but the referent itself.

        For example, in the following function f(), the first statement changes main()'s i because the formal parameter x is an alias for i. The second statement also changes i (as well as x) because the address of i is stored in y. The third statement does not change i because z is a copy of the original value of i.

         void f(int& x, int* y, int z) throw()
         {
           x = 5;   //main()'s i changed to 5
           *y = 6;  //main()'s i changed to 6
           z = 7;   //no change to main()'s i
         }
         
         int main()
         {
           int i = 4;
           f(i, &i, i);
         }
         

          FAQ 11.05 Что такое локальная ссылка?

          A local reference is a local (auto) reference variable that isn't a parameter. The following example illustrates how local references provide a temporary alias relationship. Integer reference j is an alias for integer i, so changing i to 5 changes j, and changing j changes i.

           int main()
           {
             int  i;
             int& j = i; //establish the alias relation between j and i
             i = 5;      //assigning 5 to i, changes both i and j
             j = 6;      //assigning 6 to j, changes both i and j
           }
           

          Local references are not as common as reference parameters. Local references are sometimes used to avoid recalculating the same location several times; they allow a function to attach a handle to an object that would otherwise require nontrivial address computation to access. Applications that do a lot of data cacheing sometimes use local references.

            FAQ 11.06 Что значит - вернуть ссылку?

            The function call expression is an lvalue to the referent.

            When a function returns a reference, the function call becomes an lvalue (see FAQ 11.04) for the referent. This is normally used to allow operator expressions (such as the subscript operator, the dereference operator, and so on) to be used as lvalues. The following example shows how returning a reference allows a subscript operator to be an lvalue.

             #include <iostream>
             #include <stdexcept>
             using namespace std;
             
             class Array {
             public:
               float& operator[] (unsigned i) throw(out_of_range);
             protected:
               float data_[100];
             };
             
             inline float& Array::operator[] (unsigned i)
             throw(out_of_range)
             {
               if (i >= 100u)
                 throw out_of_range(" Array index is out of range");
               return data_[i];
             }
             int main()
             {
               Array a;
               for (unsigned i = 0; i < 100; ++i)
                 a[i] = 3.14 * i;
               for (unsigned j = 0; j < 100; ++j)
                 cout << a[j] << '\n';
             }
             

            Returning a reference to data_[i] doesn't return a copy of data_[i]; it returns data_[i] itself. Therefore, anything done to the expression in the caller (a[i]) is actually done to data_[i]. In the example, the statement a[i] =...actually changes data_[i] within object a.

            C programmers should note that this allows a function call to appear on the left side of an assignment operator.

              FAQ 11.07 Чему равен адрес ссылки ?

              The address of a reference is the address of the referent.

              Remember that the reference is the referent. Anything done to the referenceincluding taking its addressis actually done to the referent. For example, the following code will print yes since &i will be equal to &j.

               #include <iostream>
               using namespace std;
               
               void sample(int* p, int* q)
               {
                 if (p == q)
                   cout << "yes\n";
                 else
                   cout << "no\n";
               }
               
               int main()
               {
                 int  i = 5;
                 int& j = i;
                 sample(&i, &j);
               }
               

                FAQ 11.08 Может ли ссылка ссылаться на другую ссылку?

                No, it can't.

                Unlike a pointer, once a reference is bound to an object, it cannot be made to refer to a different object. The alias cannot be separated from the referent.

                For example, the last line of the following example changes i to 6; it does not make the reference k refer to j. Throughout its short life, k will always refer to i.

                 int main()
                 {
                   int  i = 5;
                   int  j = 6;
                   int& k = i;                                        <-- 1
                        k = j;                                        <-- 2
                 }
                 

                (1) Bind k so it is an alias for i

                (2) Change i to 6does NOT bind k to j

                  FAQ 11.09 Зачем нужны ссылки , если есть указатели ?

                  Because references are better than pointers for some tasks.

                  Using a pointer when a reference will do is like using a chain saw to trim your fingernailsit will do the job, but you'd better be extremely careful.

                  In C, pointers are used for a variety of tasks because there is no other tool available for doing these tasks. Programmers learn to live with the dangers of pointers in C because there is no alternative. It's as if C gives you a chain saw and expects you to use it for building houses, shredding paper, trimming fingernails, and cutting hair.

                  When all that's needed is an alias for an object, a pointer is overkill, but a reference is ideal. Pointers are a powerful and valuable part of any programmer's toolbox. However, they should be used only when necessary. Don't give a chain saw to a user who just wants a manicure.

                  Use references when you can, use pointers when you have to. (See FAQ 11.11.)

                    FAQ 11.10 Является ли ссылка типом указателя ?

                    No, they are not.

                    It is important to realize that references and pointers are quite different. A pointer should be thought of as a separate object with its own distinct set of operations (*p, p->blah, and so on). So creating a pointer creates a new object. In contrast, creating a reference does not create a new object; it merely creates an alternative name for an existing object. Furthermore the operations and semantics for the reference are defined by the referent; references do not have operations of their own.

                    In the following example, notice that assigning 0 to the reference j is very different than assigning 0 to the pointer p (the 0 pointer is the same as the NULL pointer; see FAQ 1.09).

                     int main()
                     {
                       int  i = 5;
                       int& j = i;                                        <-- 1
                       int* p = &i;                                       <-- 2
                     
                       j = 0;                                             <-- 3
                       p = 0;                                             <-- 4
                     }
                     

                    (1) j is an alias for i

                    (2) p is a new object, not an alias

                    (3) Changes i

                    (4) Changes p; does not affect i

                    Because of their low-level nature, pointers are poor substitutes for references. Holding on to the belief that pointers and references are the same is like saying that char* and a string class are the same or that void* and long are the samethere is a way to map from one to the other but the mapping is forced and is unnatural. Furthermore, the purpose of programming in C++ is not to write C++ programs that look just like C programs.

                    Pointers and references are not the same, even though many compilers implement them using similar assembly language instructions. This is an implementation detail that does not change the message of this FAQ.

                      FAQ 11.11 Когда нужны указатели?

                      References are usually preferred over pointers when aliases are needed for existing objects, making them useful in parameter lists and as return values. Pointers are required when it might be necessary to change the binding to a different referent or to refer to a nonobject (a NULL pointer). Pointers often show up as local variables and member objects. An example of this can be seen in the class Array that follows.

                       class Array {
                       public:
                         int& operator[] (int i);                           <-- 1
                       protected:
                         int* data_;                                        <-- 2
                       };
                       

                      (1) References in signatures can make interfaces intuitive

                      (2) Pointers as member data allow reallocation

                      The only time a parameter or return value should be a pointer is when the function needs to accept or return a sentinel value. In this case the function can accept or return a pointer and use the NULL pointer as the sentinel value.

                        FAQ 11.12 Почему некоторые ненавидят ссылки ? :-)

                        Most of the complaints we've heard about references have come from C programmers who are new to C++, and the complaints reflect a combination of teaching old dogs new tricks and the lack of good training on what references are all about.

                        It often takes C programmers time to get used to references. In the beginning, C programmers typically complain that pass-by-reference doesn't require explicit syntax in the caller's source code (for example, no & in the caller code). After using C++ for a while, however, developers realize that this is information hiding, which is an asset rather than a liability.

                        An important goal of OO technology is to enable developers to program in the language of the problem domain rather than the language of the computer. The information hiding provided by a reference is a small step in the migration.

                        In C, the maxim is "No hidden mechanism." C++ intentionally discards this C maxim since it is inconsistent with the C++ goal of programming in the language of the problem domain rather than the language of the machine. The new maxim is "Pay for it only if you use it."

                        Write C++ code in the language of the problem domain, not the language of the machine.

                          FAQ 12.01 Является ли new чем-то большим , нежели выделением памяти?

                          Yes, it also initializes the new object.

                          Assuming Fred is a known type, the expression new Fred() is a two-step operation. The first step is to allocate sizeof(Fred) bytes of memory using a memory allocator primitive called operator new(size_t nbytes) (size_t is a typedef for an unsigned integral type such as unsigned int). This memory allocation primitive is conceptually similar to but not interchangeable with malloc(size_t nbytes). The second step is to call the appropriate constructor of the class (Fred::Fred() in this case).

                          Similarly, delete p is a two-step operation: it first calls the destructor on the object *p, then it releases the memory pointed to by p using a memory deallocation primitive. This memory deallocation primitive is called operator delete(void* p) and is conceptually similar to but not interchangeable with free(void* p).

                            FAQ 12.02 Почему new лучше , чем malloc()?

                            It does more.

                            Object construction: In C++, new and delete create and destroy objects. In contrast, malloc() and free() merely allocate and deallocate memory.

                            Safety: The new operator returns a pointer of the correct type whereas the function malloc() returns a void*, which isn't type safe. The C language allows a void* to be converted to any other pointer, but this is a dangerous hole in C's type-checking system. C++ doesn't have this weakness: converting a void* to a different pointer type requires an explicit cast in C++.

                            Flexibility: The new operator can be overloaded by a class. For example, new Fred() can use a different memory allocation primitive than is used by new Wilma(). In contrast, malloc() cannot be overloaded on a class-by-class basis.

                              FAQ 12.03 Есть ли в плюсах аналог realloc() ?

                              No; and don't use realloc() directly, since bitwise copying of an object of a user-defined class is evil.

                              When realloc() needs to move data during the reallocation, it uses a bitwise copy, which is disastrous for many user-defined classes (see FAQ 30.15). C++ objects know how to copy themselves using their own copy constructors and assignment operators.

                              Never use realloc() on objects of user-defined classes. Let the objects copy themselves. Better yet, use the vector template class rather than pointers to arrays, and the vector template will take care of reallocation automatically and correctly (see FAQ 28.13).

                                FAQ 12.04 Удаляется ли указатель , созданный с помощью new , после команды free()? И то же самое для malloc() и delete?

                                No!

                                It is perfectly legal, moral, and wholesome to use malloc() and delete in the same program or to use new and free() in the same program. But it is illegal, immoral, and despicable to call free() on a pointer allocated via new or to call delete on a pointer allocated via malloc().

                                Even if it appears to work on your particular compiler on your particular machine, please don't do it. Corrupting the heap is a very subtle and disastrous thing; it's just not worth the troubleeven if the data type is a simple array of char; even if some programmers think it would be cool. Just say no.

                                  FAQ 12.05 Что удаляет команда delete p : указатель p или обьект *p?

                                  The referent *p.

                                  If verbosity were a virtue, the syntax would be changed from delete p to deleteTheThingPointedToBy p. One could argue that the current syntax is misleading, but the same abuse of English occurs with free(p) from the C language: free(p) doesn't free p; rather it frees the memory pointed to by p.

                                    FAQ 12.06 Нужно ли указатель , созданный после new Fred() , проверять на NULL?

                                    No, new Fred() never ever returns NULL. Instead, if new runs out of memory during new Fred(), it throws an exception of type bad_alloc (see FAQ 9.02).

                                    Because of this, the if test in the following example is considered bad form since it increases code size, increases code complexity, and increases testing costs, yet it adds no value (remember, exceptions guarantee that p will never be NULL).

                                     #include <new>
                                     using namespace std;
                                     
                                     class Fred { };
                                     
                                     void sample() throw(bad_alloc)
                                     {
                                       Fred* p = new Fred();
                                       if (p == NULL) {                                   <-- 1
                                         // ...
                                       }
                                       // ...
                                     }
                                     int main()
                                     { sample(); }
                                     

                                    (1) Bad Form! Shouldn't check for NULL

                                    C programmers please note that this behavior is very different from the way out-of-memory is handled by malloc(). To be safe, every call to malloc() has to be followed by an explicict if test to see if malloc() returned NULL.

                                      FAQ 12.07 Как для new вернуть NULL ?

                                      Replace new Fred() with new(nothrow) Fred(). Note that this is a step backward for the reasons already described, so this technique should not be extensively used in new C++ code. However this technique is relevant for people who have to deal with C++ software that was written before new Fred() threw exceptions.

                                      When preexception C++ code is compiled on a modern C++ compiler, the use of exceptions may cause the application to crash. In this case, there are two basic options: update the application by making it exception safe (that is, make the code do something reasonable even if something such as new throws an exception), or patch the application by replacing new Fred() with new(nothrow) Fred(). Often, but not always, it is cheaper to patch it rather than make it exception safe. The following example demonstrates this technique.

                                       #include <new>
                                       using namespace std;
                                       
                                       void sample() throw()
                                       {
                                         Fred* p = new(nothrow) Fred();                     <-- 1
                                         if (p == NULL) {                                   <-- 2
                                           // ...
                                         }
                                       }
                                       

                                      (1) Note the nothrow

                                      (2) This test is required!

                                        FAQ 12.08 Как с помощью new чистить пул обьектов при нехватке памяти ?

                                        Applications that do a lot of freestore allocations can sometimes improve performance by using global pools of recycled objects. For example, when a dynamically allocated Fred object is no longer needed, the programmer can say p->discard() rather than delete p, and the discard() member function adds the object to a static pool of Fred objects. Then when a Fred object is needed, the programmer says Fred::create() rather than new Fred(), and the static create() member function returns the first entry from the list (or returns new Fred() if the list is empty).

                                        Everything works great until memory runs low, at which point the pool of Fred objects needs to be flushed to free up available memory. It would be ideal if the runtime system would automatically call some routine such as Fred::flushPool() whenever new ran low on memory, since the pool could be flushed without any functional impact. For example, if someone wants to create a Wilma object and the system runs out of memory because there are too many recycled Fred objects in the Fred pool, the goal is to have the system automatically call Fred::flushPool(), which actually deletes all the Fred objects on the recycled list. We set up the Fred class with its pool of recycled objects:

                                         #include <new>
                                         using namespace std;
                                         
                                         class Fred {
                                         public:
                                           static Fred* create() throw(bad_alloc);            <-- 1
                                           virtual void discard() throw();                    <-- 2
                                           static bool flushPool() throw();                   <-- 3
                                         private:
                                           Fred()  throw();                                   <-- 4
                                           ~Fred() throw();                                   <-- 5
                                           void init() throw();                               <-- 6
                                           Fred* nextRecycled_;
                                           static Fred* headRecycled_;
                                         };
                                         
                                         void Fred::init() throw()
                                         {
                                           // ...                                             <-- 7
                                           nextRecycled_ = NULL;
                                         }
                                         
                                         Fred::Fred() throw()
                                         { init(); }
                                         
                                         Fred::~Fred() throw()
                                         { }                                                  <-- 8
                                         
                                         Fred* Fred::headRecycled_ = NULL;
                                         
                                         Fred* Fred::create() throw(bad_alloc)
                                         {
                                           if (headRecycled_ == NULL)
                                             return new Fred();
                                           Fred* p = headRecycled_;
                                           headRecycled_ = headRecycled_->nextRecycled_;
                                           p->init();                                         <-- 9
                                           return p;
                                         }
                                         
                                         void Fred::discard() throw()
                                         {
                                           nextRecycled_ = headRecycled_;
                                           headRecycled_ = this;
                                         }
                                         
                                         bool Fred::flushPool() throw()
                                         {
                                           bool stuffGotDeleted = (headRecycled_ != NULL);
                                           while (headRecycled_ != NULL)
                                             delete create();
                                           return stuffGotDeleted;
                                         }
                                         

                                        (1) Named Constructor Idiom; see FAQ 16.08

                                        (2) p->discard() is analogous to delete p

                                        (3) Returns true if it freed some memory

                                        (4) Constructor is private: so users can't say new Fred()

                                        (5) Destructor is private: so users can't say delete p

                                        (6) Initializes (or possibly reinitializes) the object as if it was just created

                                        (7) This is where the constructor's logic should go

                                        (8) The destructor doesn't need to do anything with the recycled list

                                        (9) Reinitializes the object as if it were just created

                                        First, notice how users are prevented from saying new Fred() or delete p. Instead users must say Fred::create() and p->discard(), respectively. The discard() member function adds the object to the recycled pool, and the create() static member function uses the recycled pool if it isn't empty. Finally the flushPool() static member function flushes the pool of recycled Fred objects, and returns a bool indicating whether anything actually was deleted.

                                        Next, to acomplish the larger goal of having the system automatically call Fred::flushPool() whenever new runs out of memory, a special function is created that calls Fred::flushPool() (and possibly other similar pools, such as Wilma::flushPool()). This special function is known as a new handler and is called flushAllPools() in the following example. If operator new(size_t nbytes) runs out of memory, it calls this function, which tries to delete some unneeded memory. If the new handler succeeds at freeing up some storage, it simply returns to operator new(size_t nbytes), and operator new(size_t nbytes) tries the allocation again. If the new handler is unsuccessful at freeing up storage, it avoids an infinite loop by throwing an exception:

                                         #include <new>
                                         using namespace std;
                                         
                                         void flushAllPools() throw(bad_alloc)
                                         {
                                           unsigned n = 0;
                                           n += Fred::flushPool();
                                           // Flush any other pools as well,
                                           // e.g., n += Wilma::flushPool();
                                           if (n == 0) throw bad_alloc();  // Nobody freed memory;
                                                                           // prevent infinite loop
                                         }
                                         

                                        The final step is to register the function flushAllPools() as the official new handler. This is done using the set_new_handler() function and is normally called very early in the application's execution:

                                         int main()
                                         {
                                           set_new_handler(flushAllPools);                    <-- 1
                                           // ...
                                         }
                                         

                                        (1) Install the "new handler"

                                        The rest is automatic: if someone says new Barney() and the underlying allocator (operator new(size_t nbytes)) runs out of memory, the allocator automatically calls the new handler (flushAllPools()), which flushes the Fred pool (Fred::flushPool()). If something actually was flushed, the new handler returns to operator new(size_t), which tries again. If it fails a second time, the whole process repeats. Eventually one of two things happens: either operator new(size_t) succeeds, in which case the caller who said new Barney() will never know that any of this ever happened, or flushAllPools() fails to flush anything and throws an exception (in which case the new Barney() attempt vanishes, and control goes to the appropriate catch handler; see FAQ 9.03). In either case, the users who are saying new Barney() don't know anything about the pool mechanismit is invisible to them.

                                          FAQ 12.09 Что произойдет , если вызвать delete p , когда p = NULL?

                                          Nothing.

                                          Calling delete p when p is NULL is safe and is guaranteed to do nothing. This simplifies code that uses delete by letting programmers say delete p; rather than if (p != NULL) delete p;. For example,

                                           class Fred { };
                                           
                                           void sample(Fred* p) throw()
                                           {
                                             #if 0
                                               if (p != NULL)                                   <-- 1
                                                 delete p;
                                             #else
                                               delete p;                                        <-- 2
                                             #endif
                                           }
                                           
                                           int main()
                                           {
                                             sample(new Fred());
                                             sample(NULL);
                                           }
                                           

                                          (1) Bad form!

                                          (2) Good form!

                                          There are two problems with the explicit if test: first, some people get the test backwards (e.g., they say if (!p) delete p; which is backward), and second, if tests significantly increase the cost of testingto achieve branch point coverage, both the "if true" and the "if false" branches of every if need to be exercised. Thus, adding unnecessary branch points, such as if statements, to an application causes the creation of unnecessary test cases. Conversely, if a branch point can be removed from the software without complicating or invalidating something else, in general the expected quality of the software goes up and the testing cost goes down.

                                            FAQ 12.10 Что будет , если указатель удалить дважды ? :-)

                                            Catastrophe.

                                            Suppose there is a pointer variable p. The first time delete p is executed, the object *p is safely destructed and the memory pointed to by p is safely returned to the heap. The second time the same pointer is passed to delete without a subsequent new that returned that pointer, the remains of what used to be an object at *p are passed to the destructor (which could be disastrous), and the memory pointed to by p is handed back to the heap a second time. This is likely to corrupt the heap and its list of free memory. The following example illustrates this situation.

                                             class Fred { };
                                             
                                             int main()
                                             {
                                               Fred* p1 = new Fred();
                                               Fred* p2 = p1;
                                               delete p1;
                                               delete p2;                                         <-- 1
                                             }
                                             

                                            (1) Delete the same pointer twice: DISASTER!

                                              FAQ 12.11 Как создать и удалить массив ?

                                              The best way is to be radical. Instead of trying to use an array pointer correctly, it is easier (and often more efficient) not to use explicit pointers at all but instead to use a container template such as vector (see FAQ 28.13). Please don't use explicit pointers unless it is necessary. Pointers are a source of a lot of errors; using a good container class library can eliminate many pointer errors.

                                              If it is necessary or desired to use pointers anyway, the right way to allocate an array of things is with p = new Fred[n]. When the array is deallocated, the [] must appear just after the delete keyword, such as delete[] p;. Here is an example.

                                               class Fred { };
                                               
                                               int main()
                                               {
                                                 Fred* p = new Fred[100];                           <-- 1
                                                 delete[] p;                                        <-- 2
                                               }
                                               

                                              (1) Allocate an array of Fred

                                              (2) [] is required when deallocating the array

                                              The purpose of the syntactic difference between delete p and delete[] p is to distinguish deleting a thing from deleting an array of things. This is because there is no syntactic difference between the type "pointer to a thing" (Fred*) and the type "pointer to the first element of an array of things" (Fred*). This is a feature that C++ inherited from C.

                                              After all this, recall that the real solution is to not use pointers at all but instead to use a good container class library. For example, when an array of things is needed, use a container class that implements an array of things, such as the standard template vector (see FAQ 28.13).

                                                FAQ 12.12 А что будет , если применить delete p (вместо delete[] p) для удаления массива new Fred[n]?

                                                Catastrophe.

                                                It is the programmer's responsibilitynot the compiler'sto verify that the connection between new[] and delete[] is correct. If it is wrong, don't expect either a compiler error message or a clean runtime exception. Expect a disaster. Worse, the disaster might not show up during testing; it might not show up until after the software is in the field.

                                                For example, some implementations immediately corrupt the heap when the [] is omitted when deleting an array of objects; other implementations fail to destruct all but the first object in the array. The latter could cause memory leaks if the destructors release memory, cause deadlock if the destructors unlock semaphores, compromise system integrity in other ways, or trigger some combination of any or all of these.

                                                Remember that this headache can be instantly eliminated if container classes, such as vector, are used instead of raw pointers. Please use raw pointers only when it's absolutely necessary.

                                                  FAQ 12.13 Можно ли опустить [] в delete[] p для указателя p на массив встроенного типа char?

                                                  No; there is no reason to do this and it risks an avoidable disaster.

                                                  Some programmers tragically think that the [] in the delete[] p exists only so that the compiler will call the appropriate number of destructors. Following this reasoning, they assume that the [] are optional when the array is of some built-in type such as an array of char:

                                                   #include <new>
                                                   using namespace std;
                                                   
                                                   void sample(int n) throw(bad_alloc)
                                                   {
                                                     char* p = new char[n];
                                                     // ...
                                                     delete p;                                          <-- 1
                                                   }
                                                   
                                                   int main()
                                                   { sample(10); }
                                                   

                                                  (1) ERROR! Should be delete[] p;

                                                  The delete p; line above is wrong, and it can cause a disaster at runtime. In particular, the underlying deallocation primitive called for delete p; is operator delete(void*), but the deallocation primitive called for delete[] p; is operator delete[](void*). The default behavior for the latter is to call the former, but users are allowed to replace the latter with a different behavior. For example, someone might replace operator new[](size_t) (the allocation primitive called for new char[n]) and operator delete[](void*) with a separate heap from operator new(size_t) and operator delete(void*). If that happens, the delete p; line sends the pointer to the wrong heap, which could result in a disaster at runtime.

                                                  Remember: use container classes rather than raw pointers. This example could use the standard string class or perhaps something like the standard vector template.

                                                    FAQ 12.14 Можно ли для обьекта указать тип размещения в памяти , в которой он будет создан ?

                                                    With the placement syntax of the new operator, also known as placement new.

                                                    Objects are normally created on the stack, on the heap, or in static memory. These correspond to automatic allocation, dynamic allocation, and static allocation, respectively. But these normal techniques don't allow the programmer to specify the exact address at which the object will live.

                                                    Occasionally an object's desired location is known before the object is created, such as when the hardware uses a piece of storage as a way of communicating with the software. In these cases, placement new can be used.

                                                    The following example places an object of class Fred at the hexadecimal address 0xFEEDBABE and passes (42, 42) to the Fred constructor.

                                                     #include "Fred.hpp"                                  <-- 1
                                                     #include <new>
                                                     using namespace std;
                                                     
                                                     void sample()
                                                     {
                                                       void* place = (void*) 0xFEEDBABE;
                                                       Fred* p = new(place) Fred(42, 42);
                                                       // ...
                                                     }
                                                     

                                                    (1) Pretend this defines class Fred

                                                    The storage pointed to by place must be large enough to hold sizeof(Fred) bytes and must be properly aligned to hold a Fred object. The returned pointer p is numerically the same as place, but p is a Fred* rather than a void*.

                                                      FAQ 12.15 Как гарантировано создавать обьекты класса только с помощью new ?

                                                      The class can make all of its constructors private: or protected: and can provide static create() member functions. The copy constructor should also be made private: or protected:, even if it doesn't need to be defined otherwise (see FAQ 30.06). The static (or friend) create() functions then create the object using new and return a pointer to the allocated object. Here's an example.

                                                       #include <new>
                                                       #include <memory>
                                                       using namespace std;
                                                       
                                                       class Fred;  // Forward declaration
                                                       typedef  auto_ptr<Fred>  FredPtr;
                                                       
                                                       class Fred {
                                                       public:
                                                         static FredPtr create()              throw(bad_alloc);
                                                         static FredPtr create(int i)         throw(bad_alloc);
                                                         static FredPtr create(const Fred& x) throw(bad_alloc);
                                                         virtual void goBowling();
                                                       private:
                                                         Fred(int i=10)      throw();
                                                         Fred(const Fred& x) throw();
                                                         int i_;
                                                       };
                                                       FredPtr Fred::create()              throw(bad_alloc)
                                                                                           { return new Fred();  }
                                                       FredPtr Fred::create(int i)         throw(bad_alloc)
                                                                                           { return new Fred(i); }
                                                       FredPtr Fred::create(const Fred& x) throw(bad_alloc)
                                                                                           { return new Fred(x); }
                                                       Fred::Fred(int i)         throw() : i_(i)    { }
                                                       Fred::Fred(const Fred& x) throw() : i_(x.i_) { }
                                                       
                                                       void sample()
                                                       {
                                                         FredPtr p(Fred::create(5));
                                                         p->goBowling();
                                                       }
                                                       

                                                      Note that derived classes can't be instantiated since all of the constructors are private:. Derived classes could be instantiated only if some of the constructors were protected:.

                                                      FAQ 12.16 Как удаляются обьекты , созданные с помощью new ?

                                                      By explicitly calling the object's destructor. This is about the only time a destructor is called explicitly (see FAQ 20.10). For example, if p is a Fred* that was returned from placement new, *p can be destructed as follows.

                                                       #include <new>
                                                       using namespace std;
                                                       
                                                       class Fred {
                                                       public:
                                                         Fred(int i, int j) throw();
                                                         virtual ~Fred() throw();
                                                       };
                                                       
                                                       Fred* construct(void* place) throw()
                                                       {
                                                         Fred* p = new(place) Fred(42, 42);
                                                         return p;
                                                       }
                                                       
                                                       void destruct(Fred* p) throw()
                                                       {
                                                         p->~Fred();                                        <-- 1
                                                       }
                                                       void sample() throw()
                                                       {
                                                         void* place = (void*) 0xFEEDBABE;
                                                         Fred* p = construct(place);
                                                         // ...
                                                         destruct(p);
                                                       }
                                                       

                                                      (1) Do this only with placement new

                                                      Caution: Do not explicitly call the destructor of an object that will later be automatically destroyed, such as an object on the stack, an object on the heap that will be deleted, or a static object. The only time a destructor should be called explictly is when the programmer is in total control of the storage allocation and lifetime of the objectin other words, only with objects initialized by the placement new syntax.

                                                        FAQ 12.17 При создании p = new Fred(), произойдет ли утечка памяти , если в конструкторе возникнет исключение ?

                                                        No, the system straightens things out automatically.

                                                        If an exception occurs in the Fred constructor during p = new Fred(), the sizeof(Fred) bytes that were allocated are automatically released back to the heap. This is because new Fred() is a two-step process.

                                                        1. sizeof(Fred) bytes of memory are allocated using the memory allocation primitive void* operator new(size_t nbytes). This primitive is similar in spirit to malloc(size_t nbytes) (however operator new(size_t) and malloc(size_t) are not interchangeable; the two memory allocation primitives may not even use the same heap!). Recall that size_t is a typedef for some unsigned integral type such as unsigned int. Many system headers cause this typedef to be defined.

                                                        2. A Fred object is constructed in the returned memory location by calling the Fred constructor. Thus the pointer returned from the first step is passed as the constructor's this parameter. The call to the constructor is conceptually wrapped in a try block so that the memory can be released if the constructor throws an exception.

                                                        Thus the compiler generates code that looks something like that shown in following function sample().

                                                         #include <new>
                                                         using namespace std;
                                                         
                                                         class Fred {
                                                         public:
                                                           Fred() throw();
                                                           virtual ~Fred() throw();
                                                         };
                                                         
                                                         void sample() throw(bad_alloc)
                                                         {
                                                           // Original code:  Fred* p = new Fred();
                                                           Fred* p = (Fred*) operator new(sizeof(Fred));      <-- 1
                                                           try {
                                                             new(p) Fred();                                   <-- 2
                                                           }
                                                           catch (...) {
                                                             operator delete(p);                              <-- 3
                                                             throw;                                           <-- 4
                                                           }
                                                         }
                                                         

                                                        (1) Step 1: Allocate memory

                                                        (2) Step 2: Construct the object (see FAQ 12.14)

                                                        (3) Deallocate the memory

                                                        (4) Rethrow the exception the constructor threw

                                                        The statement new(p) Fred(); is called the placement new syntax (see FAQ 12.14). The effect is to call the Fred constructor, passing the pointer p as the constructor's this parameter.

                                                          FAQ 12.18 Нормально ли для члена функции выполнять delete this?

                                                          Yes, but be careful.

                                                          Programmers usually have a hard time emotionally accepting that this is valid, probably because it seems as if the member function is inside the object and deleting the object during a member function seems strange. But with care, this technique can be perfectly safe. Here is how we define "with care."

                                                          1. The this object must have been allocated via new (see FAQ 12.15), not by new[] (see FAQ 12.11) nor by placement new (see FAQ 12.14) nor by a local object on the stack nor by a global nor by a member of another object. It has to have been allocated by plain, ordinary new.

                                                          2. The member function that contains delete this; must be the last member function that is invoked on the this object.

                                                          3. The remainder of the member function after the delete this; line must not touch any piece of the this object, including calling any other member functions or touching any data members.

                                                          4. No other piece of code should even examine the this pointer itself after the delete this; line. No one may examine it, compare it with another pointer, compare it with NULL, print it, cast it, do anything with it.

                                                          5. Make sure no one else does a delete on the object. For example, if the object is still being held by an auto_ptr (which would be a good thing!), the release() member function must be called on the auto_ptr; otherwise the auto_ptr will delete the object again, which would be a disaster. For example:

                                                             #include <iostream>
                                                             using namespace std;
                                                             
                                                             class Fred {
                                                             public:
                                                               Fred();
                                                               virtual ~Fred();
                                                               virtual void discard() throw();
                                                             };
                                                             
                                                             void Fred::discard() throw()
                                                             { delete this; }
                                                             

                                                            FAQ 12.19 После создания p = new Fred[n], откуда компилятор знает , что надо удалить n обьектов для команды delete[] p?

                                                            Warning: This FAQ is quite low level. The only people who really need to worry about this level of detail are those in extremely performance sensitive situations where CPU cycles are at a premium. Of course it also might be interesting to those who are just plain curious...

                                                            Whenever someone says Fred* p = new Fred[n], the runtime system is required to store the number of objects, n, in a place that can be retrieved knowing only the pointer, p. The compiler can use any technique it wants to use, but there are two popular ones.

                                                            1. The code generated by p = new Fred[n] might store the number n in a static associative array, where the pointer p is used as the lookup key and the number n is the associated value. For example, using the standard map template (see FAQ 28.14), this associative array might be map<void*,size_t>. The code generated by delete[] p would look up the pointer in the associative array, would extract the associated size_t, then would remove the entry from the associative array.

                                                            2. The code generated by p = new Fred[n] might allocate an extra sizeof(size_t) bytes of memory (possibly plus some alignment bytes) and put the value n just before the first Fred object. Then delete[] p would find n by looking at the fixed offset before the first Fred object (that is, before *p) and would deallocate the memory starting at the beginning of the allocation (that is, the block of memory beginning the fixed offset before *p).

                                                            Neither technique is perfect. Here are a few of the tradeoffs.

                                                            1. The associative array technique is slower but safer: if someone forgets the [] when deallocating an array of things, (a) the entry in the associative array would be a leak, and (b) only the first object in the array would be destructed. This may or may not be a serious problem, but at least it might not crash the application.

                                                            2. The overallocation technique is faster but more dangerous: if someone says delete p where they should have said delete[] p, the address that is passed to operator delete(void* p) would not be a valid heap allocationit would be at least sizeof(size_t) bytes after a valid heap allocation. This would probably corrupt the heap. Bang, you're dead.

                                                              FAQ 13.01 Для чео нужны inline функции?

                                                              In some cases, inline functions make a compute-bound application run faster.

                                                              In a broad sense, the idea behind inline functions is to insert the code of a called function at the point where the function is called. If done carefully, this can improve the application's performance in exchange for increased compile time and possibly (but not always) an increase in the size of the generated binary executables. As usual, the devil is in the details. Read the fine print; it does make a difference.

                                                              It is useful to distinguish between "inline," a keyword qualifier that is simply a request to the compiler, and "inlined," which refers to actual inline expansion of the function. The expansion is more important than the request, because the costs and benefits are associated with the expansion.

                                                              Fortunately C++ programmers normally don't need to worry whether an inline function actually is inlined, since the C++ language guarantees that the semantics of a function cannot be changed by the compiler just because it is or isn't inlined. This is an important guarantee that changes the way compilers might optimize code otherwise.

                                                              C programmers may notice a similarity between inline functions and #define macros. But don't push that analogy too far, as they are different in some important ways. For example, since inline functions are part of the language, it is generally possible to step through them in a debugger, whereas macros are notoriously difficult to debug. Also, macro parameters that have side effects can cause surprises if the parameter appears more than once (or not at all) in the body of the macro, whereas inline functions don't have these problems. Finally, macros are always expanded, but inline functions aren't always inlined.

                                                                FAQ 13.02 Какая связь между словами "inline" и "inlined" functions?

                                                                Fans of Alice's Adventures in Wonderland will appreciate that a function decorated with the keyword inline may not be inlined, but an inlined function may not have been specified as inline, while the only sure way to be inlined is to not exist at all!

                                                                A function can be decorated with the inline keyword either in the class definition or in its own definition if the definition physically occurs before the function is invoked. The compiler does not promise that an inline function will be inlined, and the details of when/when not are compiler-dependent. For example, many compilers won't actually inline recursive and/or very long inline functions. So an inline function may not be inlined.

                                                                On the other hand, any function that is defined within the class body will be treated implicitly as an inline function with or without the explicit inline keyword. Again, there is no guarantee that it will be inlined, but the compiler will try anyway. So some functions are inlined without the inline keyword.

                                                                Finally, compiler-generated default constructors, copy constructors, destructors, and assignment operators are treated as inline, and often (but not always) they end up being inlined because they usually don't do anything tricky. These are the functions that are usually inlined, and they don't even explicitly appear in the code.

                                                                  FAQ 13.03 Есть какие-то специальные правила для inlining?

                                                                  Yes. Here are a few rules about inlining.

                                                                  1. Any source file that contains usage of an inline function must contain the function's definition.

                                                                  2. An inline function must be identically defined everywhere. The easy way to do this is to define it once, preferably in the class header file, and include the definition as needed. The hard way is to carefully redefine the function everywhere and learn the one-definition rule (see FAQ 13.04). But even the easy way has a potential glitch, so read FAQ 13.04 regardless.

                                                                  3. main() cannot be inline.

                                                                  But these are just language rules that tell how to do inline functions. To find out when to do inline functions, read the rest of this chapter.

                                                                    FAQ 13.04 Что такое one-definition rule (ODR)?

                                                                    The ODR says that C++ constructs must be identically defined in every compilation unit in which they are used.

                                                                    For example, if source file foo.cpp and source file bar.cpp are in the same program and class Fred appears in both source files, then class Fred has to be identically defined in the two source files. Similarly, the definition of an inline function must be the same in the two source files.

                                                                    Here's what "identically defined" means. With respect to the ODR, two definitions contained in different source files are said to be identically defined if and only if they are token-for-token identical and the tokens have the same meaning in both source files. The last clause is important: it warns that merely including the same definition in two places isn't good enough to prevent ODR problems, because the tokens (such as typedefs) can have different meanings in the different locations. Note that "identically defined" does not mean character-by-character equivalence. For example, two definitions can have different whitespace or different comments yet still be "identical."

                                                                      FAQ 13.05 Как влияют на производительность inline functions?

                                                                      Here are some important facts about inlined functions.

                                                                      1. They might improve performance, or they might make it worse (see FAQ 13.06).

                                                                      2. They might cause the size of the executable to increase, or they might make it smaller (see FAQ 13.07).

                                                                      3. When they are nontrivial, they probably will complicate the development effort if employed too early in the development cycle. Provisions to use them can be made early but the final decision should be made later (see FAQ 13.08).

                                                                      4. When they are provided to third-party programmers, they can make it difficult to maintain binary compatibility between releases of the software (see FAQ 13.09).

                                                                      The only dangerous idea about inlining is the attitude that says, "Fast is good, slow is bad; inlining is faster, so everything should be inlined." If only life were that simple.

                                                                        FAQ 13.06 Улучшают ли inlined functions производительность?

                                                                        Inlined functions sometimes improve overall performance, but there are other cases where they make it worse.

                                                                        It is reasonable to define functions as inline when they are on the critical path of a CPU-bound application. For example, if a program is network bound (spending most of the time waiting for the network) or I/O bound (spending most of the time waiting for the disk), inlining might not make a significant impact in the application's performance since processor performance might not be relevant. Even in cases when the program is CPU bound, there is no point in worrying about optimizing a function unless it is on a critical execution path, that is, unless it is taking up a major portion of the time consumed by the program.

                                                                        In cases where processor optimization is relevant, inline functions can speed up processing in three ways:

                                                                        1. Eliminating the overhead associated with the function call instruction

                                                                        2. Eliminating the overhead associated with pushing and popping parameters

                                                                        3. Allowing the compiler to optimize the code outside the function call more efficiently

                                                                        The first point is worth pursuing only in very exceptional circumstances because the overhead of the call instruction itself is normally quite small. However, the call instruction can have some expensive side effects that can be eliminated when the call is inlined. For example, if the called routine is not inlined, accessing the called routine's executable code can cause a page fault (a page fault happens in virtual memory operating systems when some chunk of the application is temporarily stored on disk rather than in memory). In extreme cases, this can cause a situation called thrashing, a situation in which most of the time is spent handling page faults. Even when a page fault does not occur (that is, when the executable code of the called routine is already in memory), the code of the called routine might not be in the processor's cache, a situation called a cache miss. Cache misses can be expensive on certain CPU-bound applications.

                                                                        The second point can be important, because value semantics can have considerable overhead. A function with many formal parameters, whether they use value semantics or not, can do quite a bit of stack pushing and popping. Even if the parameter is a simple built-in type such as int or char, there is some nontrivial overhead. For example, parameters of built-in types are often located in one of the processor's registers, so the caller has to move the parameter out of the register onto the stack, and the called routine often pulls it back off the stack into a register, and when the call is finished, the caller pulls it back off the stack into its original register. All that can normally be eliminated if the call is inlined.

                                                                        The third point, also known as procedural integration, is typically the major source of improvement. By letting the compiler see more code at once (by integrating the called procedure into the calling code), optimizer can often do a better job: it can often generate more efficient code.

                                                                        But performance can be lost as well as gained. Sometimes inlined functions lead to larger binary files that cause thrashing on virtual memory operating systems (see FAQ 13.07). But there are other times where the paging performance is actually improved, because inlining can provide locality of reference and a smaller working set as well as, paradoxically, smaller executables.

                                                                          FAQ 13.07 Увеличивают ли inlined functions размер выполняемого кода?

                                                                          Sometimes yes, sometimes no.

                                                                          When large functions are inlined, the executable generally grows larger. However when small functions are inlined, the executable can actually become smaller, since the amount of executable code needed to call a function, including pushing all the registers and parameters, can be larger than the amount of code that would have been generated had the call been inlined.

                                                                          Consider the following example. With optimization turned on, the size of the generated code was 33% smaller when the member functions were inlined compared to when they were not inlined.

                                                                           class Stack {
                                                                           public:
                                                                             Stack()             throw();
                                                                             void push(int elem) throw();
                                                                             int  pop()          throw();
                                                                             int  top()   const  throw();
                                                                             bool full()  const  throw();
                                                                             bool empty() const  throw();
                                                                           protected:
                                                                             enum      { dataMax_ = 10 };
                                                                             unsigned  len_;
                                                                             int       data_[dataMax_];
                                                                           };
                                                                           
                                                                           inline      Stack::Stack()        throw() : len_(0) { }
                                                                           inline void Stack::push(int elem) throw()
                                                                                                             { data_[len_++] = elem;    }
                                                                           inline int  Stack::pop()          throw()
                                                                                                             { return data_[--len_];    }
                                                                           inline int  Stack::top()   const  throw()
                                                                                                             { return data_[len_-1];    }
                                                                           inline bool Stack::full()  const  throw()
                                                                                                             { return len_ == dataMax_; }
                                                                           inline bool Stack::empty() const  throw()
                                                                                                             { return len_ == 0;        }
                                                                           
                                                                           int main()
                                                                           {
                                                                             Stack s;
                                                                             s.push(0); s.push(1); s.push(2); s.push(3); s.push(4);
                                                                             s.push(5); s.push(6); s.push(7); s.push(8); s.push(9);
                                                                             s.pop();   s.pop();   s.pop();   s.pop();   s.pop();
                                                                             s.pop();   s.pop();   s.pop();   s.pop();   s.pop();
                                                                           }
                                                                           

                                                                            FAQ 13.08 Почему inlining оптимизацию надо делать в конце , а не в начале написания ?

                                                                            Except for trivial access member functions, the information for making intelligent inlining decisions usually comes from profiling actual code, not from the developer's intuition.

                                                                            Intuition is a crummy guide when it comes to performance. There are so many issues to consider that vary with the compiler, operating system, hardware, and system configuration that very few programmers can anticipate exactly where the bottlenecks will occur. Chapter 33, "High-Performance Software," discusses this in more detail.

                                                                            But even if the bottlenecks are known ahead of time, using inline on nontrivial functions early in the development cycle can add significant frustrations to the edit-compile-debug cycle. Since inline functions normally are defined in header files, when an inline function is changed the compiler will normally recompile every source file (for example, every .cpp file) that includes that function. This often means recompiling the world, which can take a long, long time on a medium or large project.

                                                                              FAQ 13.09 Надо ли использовать inlined function из чужих библиотек?

                                                                              It makes it harder for that third party to maintain binary compatibility between releases of the third party's software.

                                                                              The code of an inlined function is copied into the user's executables. So subsequent changes to the inlined function require recompilation of the user's code, which can be painful or even politically impossible if there is a vendor-customer relationship.

                                                                              For example, vendors who want to provide their customers with binary-compatible releases of a library must avoid changing any inline functions that are accessible to their customers. Typically this is done by considering the implementation of any inline functions that have been shipped to customers to be "frozen"frozen code can never, ever change. Recall that the compiler-synthesized construction, assignment, and destruction routines are implicitly inline (see FAQ 13.02), so achieving binary compatibility requires these routines to be explicitly defined as non-inline. For example, if the compiler defines them as inline, and if the class layout ever changes, they will change from release to release. This includes relatively innocuous changes to the class layout, such as adding or removing a private: data member.

                                                                              Another, more subtle, problem is the potential breakdown of information hiding. For example, users of an inline function must have a copy of the source code, and that copy gets bound into their program. Think about that for a second. Maybe they haven't read this book, and maybe they don't understand the importance of respecting interfaces. Maybe they rely on the implementation rather than the specification; giving them the code opens up that opportunity. If so, the implementation techniques are turned into concrete. This makes life harder.

                                                                                FAQ 13.10 Как переключаться между inline и non-inline кодом?

                                                                                Yes, with a little macro magic.

                                                                                Most projects should turn off inlining during development. That is, they use a compiler option that causes the compiler to not inline any inline functions. This can make the code easier to debug, but it still doesn't help the edit-compile-debug problem mentioned in FAQ 13.08. For example, turning off inline expansion via a compiler option does not improve compile-time performancethe compiler still has to parse the body of every inline function in a header every time a source file is compiled that includes the header. Furthermore, depending on the compiler, turning off inline expansion may increase code bulkthe compiler may create duplicate static copies of each inline function seen by the compiler during every compilation unit. Finally, and probably most important, turning off inline expansion doesn't help the "recompile the world" problem (see FAQ 13.08) since the inline functions are still in the include files.

                                                                                Although macros are evil, this is one of the areas where they can be used to bypass the compile-time overhead mentioned in FAQ 13.08. The strategy is straightforward. First, define all inline functions outside the class body in a separate file (call this the .inl file or .ipp file). Then, in the .inl file, change the keyword inline to the preprocessor symbol INLINE. Finally, conditionally #include the .inl file from either the bottom of the .hpp file or from the .cpp file, depending on whether or not INLINE should become inline or nothing.

                                                                                In the following example, inline.hpp defines a macro INLINE to be either the keyword inline or nothing, depending on whether the USE_INLINE symbol is #defined. For example, if the compiler supports the -D option as a way to #define a symbol, compiling with -DUSE_INLINE causes INLINE to become inline. Here is file inline.hpp.

                                                                                 #ifndef INLINE_HPP
                                                                                 #define INLINE_HPP
                                                                                 
                                                                                 #ifdef USE_INLINE
                                                                                   #define INLINE  inline
                                                                                 #else
                                                                                   #define INLINE  /*nothing*/
                                                                                 #endif
                                                                                 
                                                                                 #endif
                                                                                 

                                                                                File Fred.hpp defines class Fred with two member functions, f() and g(). If the symbol USE_INLINE is #defined, file Fred.inl is #included from Fred.hpp. Here is file Fred.hpp.

                                                                                 #ifndef FRED_HPP
                                                                                 #define FRED_HPP
                                                                                 
                                                                                 #include "inline.hpp"
                                                                                 
                                                                                 class Fred {
                                                                                 public:
                                                                                   void f() throw();                                  <-- 1
                                                                                   void g() throw();
                                                                                 };
                                                                                 
                                                                                 #ifdef USE_INLINE                                    <-- 2
                                                                                   #include "Fred.inl"
                                                                                 #endif
                                                                                 
                                                                                 #endif
                                                                                 

                                                                                (1) No code in the class body

                                                                                (2) #ifdef means "if defined"

                                                                                File Fred.inl defines Fred::f() preceded with the symbol INLINE. Note that Fred.inl does not #include "Fred.hpp". Here is file Fred.inl.

                                                                                 #include <iostream>
                                                                                 using namespace std;
                                                                                 
                                                                                 INLINE void Fred::f() throw()                        <-- 1
                                                                                 { cout << "Fred::f() is optionally inlined\n"; }
                                                                                 

                                                                                (1) Uses INLINE, not inline

                                                                                File Fred.cpp defines Fred::g() as non-inline. If the symbol USE_INLINE is not #defined, file Fred.inl is #included from Fred.cpp. Here is file Fred.cpp.

                                                                                 #include "Fred.hpp"
                                                                                 using namespace std;
                                                                                 
                                                                                 #ifndef USE_INLINE                                   <-- 1
                                                                                   #include "Fred.inl"
                                                                                 #endif
                                                                                 
                                                                                 void Fred::g() throw()
                                                                                 { cout << "Fred::g() is never inlined\n"; }
                                                                                 

                                                                                (1) #ifndef means "if not defined"

                                                                                It is important to note that users of Fred don't have to be aware of the .inl file. For example, if file UserCode.cpp uses a Fred object, it won't need to change due to the INLINE magic. Here is a sample file that uses a Fred object, file UserCode.cpp.

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

                                                                                This strategy can be easily modified to allow class-specific inlining. Simply replace the line #include "inline.hpp" with the contents of that file, then change USE_INLINE to USE_INLINE_Fred and INLINE to INLINE_Fred throughout.

                                                                                  FAQ 14.01 Как правильно продекларировать указатель ?

                                                                                  Pointer declarations should be read right to left.

                                                                                  If Fred is some type, then

                                                                                  • Fred* is a pointer to a Fred (the * is pronounced "pointer to a").

                                                                                  • const Fred* is a pointer to a Fred that cannot be changed via that pointer.

                                                                                  • Fred* const is a const pointer to a Fred. The Fred object can be changed via the pointer, but the pointer itself cannot be changed.

                                                                                  • const Fred* const is a const pointer to a Fred that cannot be changed via that pointer.

                                                                                  References are similar: read them right to left.

                                                                                  • Fred& is a reference to a Fred (the & is pronounced "reference to a").

                                                                                  • const Fred& is a reference to a Fred that cannot be changed via that reference.

                                                                                  Note that Fred& const and const Fred& const are not included in the second list. This is because references are inherently immutable: you can never rebind the reference so that it refers to a different object.

                                                                                    FAQ 14.02 Как предотвратить изменение обьекта ?

                                                                                    With proper use of the keyword const, the C++ compiler detects many unexpected changes to objects and flags these violations with error messages at compile time. This is often called const correctness. For example, function f() uses the const keyword to restrict itself so that it won't be able to change the caller's string object:

                                                                                     #include <string>
                                                                                     using namespace std;
                                                                                     void f(const string& s) throw();  // Parameter is received
                                                                                                                       // by reference-to-const
                                                                                     

                                                                                    If f() changes its passed string anyway, the compiler flags it as an error at compile time:

                                                                                     void f(const string& s) throw()
                                                                                     {
                                                                                       #ifdef GENERATE_ERROR
                                                                                         s += "foo";                                      <-- 1
                                                                                       #endif
                                                                                     }
                                                                                     

                                                                                    (1) Error: The string cannot be mutated via s

                                                                                    In contrast, function g() declares its intent to change the caller's string object by its lack of the const keyword in the appropriate place:

                                                                                     void g(string& s) throw();  // Parameter is received by
                                                                                                                 //  reference-to-non-const
                                                                                     

                                                                                    For example, it is legal and appropriate for g() to modify the caller's string object:

                                                                                     void g(string& s) throw()
                                                                                     {
                                                                                       s += "foo";  // OK: Modifies the caller's string object
                                                                                     }
                                                                                     

                                                                                    Also it would be legal and appropriate for g() to pass its parameter to f(), since the called function, f(), is at least as restrictive as the caller, g() (in this case, the called function is actually more restrictive):

                                                                                     void g(string& s) throw()
                                                                                     {
                                                                                       f(s);        // OK (though it doesn't happen to modify
                                                                                                    // caller's string object)
                                                                                       s += "foo";  // OK: Modifies the caller's string object
                                                                                     }
                                                                                     

                                                                                    However, it would be illegal for the opposite to occur. That is, if f() passed its parameter to g(), the compiler would give an error message since the called function, g(), is less restrictive than the caller, f():

                                                                                     void f(const string& s) throw()
                                                                                     {
                                                                                       #ifdef GENERATE_ERROR
                                                                                         g(s);                                            <-- 1
                                                                                       #endif
                                                                                     }
                                                                                     

                                                                                    (1) Error: The const string& cannot be passed as a string&

                                                                                      FAQ 14.03 Улучшает ли const runtime overhead?

                                                                                      No, there is no runtime overhead. All tests for constness are done at compile time. Neither runtime space nor speed is degraded.

                                                                                        FAQ 14.04 const позволяет компилятору генерировать более эффективный код ?

                                                                                        Occasionally, but that's not the purpose of const. The purpose of const is correctness, not optimization. That is, const helps the compiler find bugs, but it does not (normally) help the compiler generate more efficient code.

                                                                                        Declaring the constness of a parameter or variable is just another form of type safety; therefore, const correctness can be considered an extension of C++'s type system. Type safety provides some degree of semantic integrity by promising that, for instance, something declared as a string cannot be used as an int. However, const correctness guarantees even tighter semantic correctness by making sure that data that is not intended to be changed cannot be changed. With const correctness, it is easier to reason about the correctness of the software. This is helpful during software inspection.

                                                                                        It is almost as if const string and string are of different, but related, classes. Because type safety helps produce correct software (especially in large systems and applications), const correctness is a worthy goal.

                                                                                          FAQ 14.05 Is const correctness tedious?

                                                                                          It is no more tedious than declaring the type of a variable.

                                                                                          In C++, const correctness is simply another form of type information. In theory, expressing any type information is unnecessary, given enough programmer discipline and testing. In practice, developers often leave a lot of interesting information about their code in their heads where it cannot be exploited or verified by the compiler. For instance, when programmers write a function such as the following print() function, they know implicitly that they are passing by reference merely to avoid the overhead of passing by value; there is no intention of changing the string during the print() operation.

                                                                                           #include <string>
                                                                                           using namespace std;
                                                                                           
                                                                                           void print(string& s);  // Does not change s         <-- 1
                                                                                           

                                                                                          (1) Wrong way to document the restriction

                                                                                          If this information is documented only by comments in the code or in a separate manual, it is easy for these comments to become inconsistent with the code; the compiler can't read comments or manuals. The most cost-effective way to document this information is with the five-letter word const:

                                                                                           void print(const string& s);                         <-- 1
                                                                                           

                                                                                          (1) Right way to document the restriction

                                                                                          This form of documentation is succinct, in one place, and can be verified and exploited by the compiler.

                                                                                            FAQ 14.06 Почему const нужно инициализировать в начале ?

                                                                                            Adding constraints later can be difficult and expensive. If a function was not originally restricted with respect to changing a by-reference parameter, adding the restriction (that is, changing a parameter from string& to const string&) can cause a ripple through the system. For instance, suppose f() calls g(), and g() calls h():

                                                                                             #include <string>
                                                                                             #include <iostream>
                                                                                             using namespace std;
                                                                                             
                                                                                             void f(string& s) throw();
                                                                                             void g(string& s) throw();
                                                                                             void h(string& s) throw();
                                                                                             
                                                                                             int main()
                                                                                             {
                                                                                               string s;
                                                                                               f(s);
                                                                                             }
                                                                                             
                                                                                             void f(string& s) throw()
                                                                                             { g(s); }
                                                                                             
                                                                                             void g(string& s) throw()
                                                                                             { h(s); }
                                                                                             
                                                                                             void h(string& s) throw()
                                                                                             { cout << s << '\n'; }
                                                                                             

                                                                                            Changing f(string& s) to f(const string& s) causes error messages until g(string&) is changed to g(const string&). But this change causes error messages until h(string&) is changed to h(const string&), and so on. The ripple effect is magnificentand expensive.

                                                                                            The moral is that const correctness should be installed from the very beginning.

                                                                                              FAQ 14.07 В чем разница между inspector и mutator?

                                                                                              An inspector is a member function that returns information about an object's state without changing the object's abstract state (that is, calling an inspector does not cause an observable change in the behavior of any of the object's member functions). A mutator changes the state of an object in a way that is observable to outsiders: it changes the object's abstract state. Here is an example.

                                                                                               class Stack {
                                                                                               public:
                                                                                                 int pop();             //Mutator
                                                                                                 int numElems() const;  //Inspector
                                                                                               };
                                                                                               

                                                                                              The pop() member function is a mutator because it changes the Stack by removing the top element. The numElems() member function is an inspector because it simply counts the number of elements in the Stack without making any observable changes to the Stack. The const decoration after numElems() indicates that numElems() promises not to change the Stack object.

                                                                                              Only inspectors may be called on a reference-to-const or pointer-to-const:

                                                                                               void sample(const Stack& s) throw()
                                                                                               {
                                                                                                 s.numElems();  // OK: A const Stack can be inspected
                                                                                               
                                                                                                 #ifdef GENERATE_ERROR
                                                                                                   s.pop();     // Error: A const Stack cannot be mutated
                                                                                                 #endif
                                                                                               }
                                                                                               

                                                                                                FAQ 14.08 В каких случаях член-функцию нужно декларировать как const?

                                                                                                There are two ways to look at it. When looking at the member function from the inside out, the answer is "whenever the member function wants to guarantee that it won't make any observable changes to its this object." When looking at the member function from the outside in, the answer is "whenever a caller needs to invoke the member function via a reference-to-const or pointer-to-const." Hopefully these two approaches end up agreeing with each other. If not, then the application may have a serious design flaw, or perhaps it needs to be const overloaded (that is, two member functions with the same name and the same parameters, but one is a const member function and the other is not).

                                                                                                The compiler won't allow a const member function to change *this or to invoke a non-const member function for this object:

                                                                                                 #include <stdexcept>
                                                                                                 using namespace std;
                                                                                                 
                                                                                                 class Stack {
                                                                                                 public:
                                                                                                   Stack() throw();
                                                                                                   void push(int elem) throw(runtime_error);
                                                                                                   // Throws exception if numElems() is 10
                                                                                                   int  pop()          throw(runtime_error);
                                                                                                   // Throws exception if numElems() is 0
                                                                                                   int  numElems() const throw();
                                                                                                 protected:
                                                                                                   int numElems_;
                                                                                                   int data_[10];
                                                                                                 };
                                                                                                 
                                                                                                 int Stack::numElems() const throw()
                                                                                                 {
                                                                                                   #ifdef GENERATE_ERROR
                                                                                                     ++numElems_;    //ERROR: Can't modify *this
                                                                                                     pop();          //ERROR: pop() isn't a const member function
                                                                                                   #endif
                                                                                                   return numElems_;
                                                                                                 }
                                                                                                 

                                                                                                Although not fleshed out in this example, member functions push() and pop() may throw exceptions in certain circumstances. In this case they throw runtime_error, which is the standard exception class that is thrown for errors that are detectable only at runtime.

                                                                                                  FAQ 14.09 Does const apply to the object's bitwise state or its abstract state?

                                                                                                  The const keyword should refer to the object's abstract state.

                                                                                                  The const modifier is a part of the class's public: interface; therefore, it means what the designer of the public: interface wants it to mean. As an interface designer, the most useful strategy is to tie const to the object's abstract state rather than to its bitwise state. For example, in some circumstances a member function changes its object's bitwise state, yet the change doesn't cause any observable change to any of the object's public: member functions (that is, the abstract state is not changed). In this case, the member function should still be const since it never changes the meaning of the object (see FAQ 14.12). It is even more common for a member function to change an object's abstract state even though it doesn't change the object's bitwise state.

                                                                                                  For example, the following MyString class stores its string data on the heap, pointed to by the member datum data_. (The name bad_alloc is the standard exception class that is thrown when memory is exhausted, and the name out_of_range is the standard exception class that is thrown when a parameter is out of range.)

                                                                                                   #include <new>
                                                                                                   #include <stdexcept>
                                                                                                   #include <iostream>
                                                                                                   using namespace std;
                                                                                                   
                                                                                                   class MyString {
                                                                                                   public:
                                                                                                     MyString(const char* s) throw(bad_alloc);
                                                                                                    ~MyString() throw();
                                                                                                     MyString(const MyString& s) throw(bad_alloc);
                                                                                                     MyString& operator= (const MyString& s) throw(bad_alloc);
                                                                                                     unsigned size() const throw();
                                                                                                     char& operator[] (unsigned index)       throw(out_of_range);
                                                                                                     char  operator[] (unsigned index) const throw(out_of_range);
                                                                                                     void toUpper() throw();  //capitalizes the string
                                                                                                   protected:
                                                                                                     unsigned len_;
                                                                                                     char* data_;
                                                                                                   };
                                                                                                   
                                                                                                   int main()
                                                                                                   {
                                                                                                     MyString s = "xyz";
                                                                                                     for (unsigned i = 0; i < s.size(); ++i)
                                                                                                       cout << "Character #" << i << " is " << s[i] << '\n';
                                                                                                     s.toUpper();
                                                                                                   }
                                                                                                   

                                                                                                  The abstract state of the MyString object s is represented by values returned by s[i], where i ranges from 0 to s.size()1, inclusive. The bitwise state of a MyString is represented by the bits of s itself (that is, by s.len_ and the pointer s.data_).

                                                                                                  Even though s.toUpper() doesn't change s.len_ or the pointer s.data_, MyString::toUpper() is a non-const member function because it changes the abstract state (the state from the user's perspective). In other words, toUpper() doesn't change the bitwise state of the object, but it does change the meaning of the object; therefore it is a non-const member function.

                                                                                                    FAQ 14.10 Почему const нельзя использовать для декларирования формальных параметров?

                                                                                                    Do not use const for formal parameter types that are passed by value, because a const on a pass-by-value parameter affects (constrains) only the code inside the function; it does not affect the caller. For example, replace f(const Fred x) with either f(const Fred& x) or f(Fred x).

                                                                                                    As a special case of this rule, it is inappropriate to use Fred* const in a formal parameter list. For example, replace f(Fred* const p) with f(Fred* p), and replace g(const Fred* const p) with g(const Fred* p).

                                                                                                    Finally, do not use Fred& const in any context. The construct is nonsensical because a reference can never be rebound to a different object. (See FAQ 14.01.)

                                                                                                      FAQ 14.11 В каких случаях const нельзя использовать для декларирования типа возвращаемой функции?

                                                                                                      A function that returns its result by value should generally avoid const in the return type. For example, replace const Fred f() with either Fred f() or const Fred& f(). Using const Fred f() can be confusing to users, especially in the idiomatic case of copying the return result into a local.

                                                                                                      The exception to this rule is when users apply a const-overloaded member function directly to the temporary returned from the function. An example follows.

                                                                                                       #include <iostream>
                                                                                                       using namespace std;
                                                                                                       
                                                                                                       class Fred {
                                                                                                       public:
                                                                                                         void wilma()       throw() { cout << "Fred::wilma()\n"; }
                                                                                                         void wilma() const throw() { cout << "Fred::wilma() const\n"; }
                                                                                                       };
                                                                                                       
                                                                                                             Fred f() throw() { cout << "f(): "; return Fred(); }
                                                                                                       const Fred g() throw() { cout << "g(): "; return Fred(); }
                                                                                                       
                                                                                                       int main()
                                                                                                       {
                                                                                                         f().wilma();                                       <-- 1
                                                                                                         g().wilma();                                       <-- 2
                                                                                                       }
                                                                                                       

                                                                                                      (1) Calls the non-const version of the wilma() member function

                                                                                                      (2) Calls the const version of the wilma() member function

                                                                                                      Because f() returns a non-const Fred, f().wilma() invokes the non-const version of Fred::wilma(). In contrast, g() returns a const Fred, so g().wilma() invokes the const version of Fred::wilma(). Thus, the output of this program is as follows.

                                                                                                       f(): Fred::wilma()
                                                                                                       g(): Fred::wilma() const
                                                                                                       

                                                                                                        FAQ 14.12 Как можно "nonobservable" член данных изменить внутри const члена функции ?

                                                                                                        Preferably the data member should be declared with the mutable keyword. If that cannot be done, const_cast can be used.

                                                                                                        A small percentage of inspectors need to make nonobservable changes to data members. For example, an object whose storage is physically located in a database might want to cache its last lookup in hopes of improving the performance of its next lookup. In this case there are two critical issues: (1) if someone changes the database, the cached value must somehow be either changed or marked as invalid (cache coherency); (2) the const lookup member function needs to make a change to the cached value. In cases like this, changes to the cache are not observable to users of the object (the object does not change its abstract state; see FAQ 14.09).

                                                                                                        The easiest way to implement a nonobservable change is to declare the cache using the mutable keyword. The mutable keyword tells the compiler that const member functions are allowed to change the data member.

                                                                                                         int readFromDatabase() throw()
                                                                                                         { return 42; }                                       <-- 1
                                                                                                         
                                                                                                         class Fred {
                                                                                                         public:
                                                                                                           int get() const throw();
                                                                                                         private:
                                                                                                           mutable int cache_;
                                                                                                           mutable bool cacheOk_;
                                                                                                         };
                                                                                                         
                                                                                                         int Fred::get() const throw()
                                                                                                         {
                                                                                                           if (! cacheOk_) {
                                                                                                             cache_ = readFromDatabase();
                                                                                                             cacheOk_ = true;
                                                                                                           }
                                                                                                           return cache_;
                                                                                                         }
                                                                                                         
                                                                                                         int main()
                                                                                                         {
                                                                                                           Fred f;
                                                                                                           int x = f.get();                                   <-- 2
                                                                                                           int y = f.get();                                   <-- 3
                                                                                                           int z = f.get();                                   <-- 4
                                                                                                         }
                                                                                                         

                                                                                                        (1) Pretend this reads from the database

                                                                                                        (2) Access the database the first time it's called

                                                                                                        (3) Uses the cache; no database calls

                                                                                                        (4) Uses the cache; no database calls

                                                                                                        The second alternative is to cast away the constness of the this pointer using the const_cast keyword. In the following example, self is equal to this (that is, they point to the same object), but self is a Fred* rather than a const Fred* so self can be used to modify the this object.

                                                                                                         class Fred2 {
                                                                                                         public:
                                                                                                           int get() const throw();
                                                                                                         private:
                                                                                                           int cache_;
                                                                                                           bool cacheOk_;
                                                                                                         };
                                                                                                         
                                                                                                         int Fred2::get() const throw()
                                                                                                         {
                                                                                                           if (! cacheOk_) {
                                                                                                             Fred2* self = const_cast<Fred2*>(this);
                                                                                                             self->cache_ = readFromDatabase();
                                                                                                             self->cacheOk_ = true;
                                                                                                           }
                                                                                                           return cache_;
                                                                                                         }
                                                                                                         

                                                                                                          FAQ 14.13 Может ли обьект быть изменен в случае const ссылочного указателя на него ?

                                                                                                          Yes, due to aliasing.

                                                                                                          The const part restricts the reference (pointer); it does not restrict the object. Many programmers erroneously think that the object on the other end of a const reference (pointer) cannot change. For example, if const int& i refers to the same int as int& j, j can change the int even though i cannot. This is called aliasing, and it can confuse programmers who are unaware of it.

                                                                                                           #include <iostream>
                                                                                                           using namespace std;
                                                                                                           
                                                                                                           void sample(const int& i, int& j) throw()
                                                                                                           {
                                                                                                             int orig = i;
                                                                                                             j++;                                               <-- 1
                                                                                                             if (i != orig)
                                                                                                               cout << "The value of i is different!\n";
                                                                                                           }
                                                                                                           
                                                                                                           int main()
                                                                                                           {
                                                                                                             int x = 3;
                                                                                                             sample(x, x);
                                                                                                           }
                                                                                                           

                                                                                                          (1) Incrementing j can change the int called i

                                                                                                          There is no rule in C++ that prohibits this sort of thing. In fact, it is considered a feature of the language that programmers can have several pointers or references refer to the same object (plus it could not be figured out in some cases, e.g., if there are intermediate functions between main() and sample(const int&,int&) and if these functions are defined in different source files and are compiled on different days of the week). The fact that one of those references or pointers is restricted from changing the underlying object is a restriction on the reference (or pointer), not on the object.

                                                                                                            FAQ 14.14 Применима ли оптимизация для const_cast ?

                                                                                                            No, the compiler doesn't lose optimization opportunities because of const_cast.

                                                                                                            Some programmers are afraid to use const_cast because they're concerned that it will take away the compiler's ability to optimize the code. For example, if the compiler cached data members of an object in registers, then called a const member function, in theory it would need to reload only those registers that represent mutable data members. However in practice this kind of optimization cannot occur, with or without const_cast.

                                                                                                            The reason the optimization cannot occur is that it would require the compiler to prove that there are no non-const references or pointers that point to the object (the aliasing problem; see FAQ 14.13), and in many cases this cannot be proved.

                                                                                                              FAQ 15.01 О чем 15-я глава ?

                                                                                                              This chapter covers the basic material on namespaces.

                                                                                                              The purpose of namespaces is to reduce the number of name clashes that occur with multiple, independently developed libraries. In the early days of C++, this was not as big a problem because there weren't as many C++ libraries as there are today. This situation changed about the time that the standard template library was formalized, and today the large number of class libraries and frameworks on the market accentuates the need for a mechanism to resolve name conflicts.

                                                                                                              The original goal for namespaces was to have an elegant solution that could be explained to the typical developer in ten minutes and that could be implemented by a compiler-writer in two weeks. The problem was a bit more difficult than anticipated, and, like almost every other feature in C++, it has some dark corners that most people should avoid. But the basic ideas of namespaces are technically sound and within the grasp of any professional developer.

                                                                                                                FAQ 15.02 Что такое namespace?

                                                                                                                A namespace is a declarative region that can be used to package names, improve program readability, and reduce name clashes in the global namespace. It is an elegant alternative to using prefixes to indicate the sources of names.

                                                                                                                At the most basic level, the syntax for namespaces is shown in the following example.

                                                                                                                 namespace MySubsystem {
                                                                                                                   class Fred {                                       <-- 1
                                                                                                                     // ...
                                                                                                                   };
                                                                                                                 
                                                                                                                   class Wilma {                                      <-- 2
                                                                                                                     // ...
                                                                                                                   };
                                                                                                                 
                                                                                                                   void funct()                                       <-- 3
                                                                                                                   {
                                                                                                                     // ...
                                                                                                                   }
                                                                                                                 
                                                                                                                   // Other declarations for MySubsystem go in here
                                                                                                                 }
                                                                                                                 

                                                                                                                (1) Class MySubsystem::Fred

                                                                                                                (2) Class MySubsystem::Wilma

                                                                                                                (3) Function MySubsystem::funct()

                                                                                                                Namespaces facilitate building large systems by partitioning names into logical groupings.

                                                                                                                Namespaces are "open" and can be added to at different places. For most situations, this incremental approach is probably a better alternative than trying to specify all of the names in one location, and this approach is used heavily in the standard library. However the incremental approach must be used carefully, because the effect is as if the compiler made a single pass and knew about only the names that were part of the namespace the last time the namespace was explicitly introduced into the source file.

                                                                                                                Namespaces can be nested if desired. Java has a concept similar to namespaces called packages.

                                                                                                                UML expresses namespaces as shown below (they are called packages in UML):

                                                                                                                graphics/15fig01.gif

                                                                                                                  FAQ 15.03 Может ли код снаружи использовать переменные , определенные внутри namespace?

                                                                                                                  One way to use a declaration from a namespace is, for every occurrence, to use the scope operator :: to prefix the name declared in the namespace with the name of the namespace. For example,

                                                                                                                   namespace People {
                                                                                                                     int fred = 2;
                                                                                                                     int wilma = 3;
                                                                                                                   }
                                                                                                                   
                                                                                                                   void f() throw()
                                                                                                                   {
                                                                                                                     int i = People::fred + People::wilma;
                                                                                                                   }
                                                                                                                   

                                                                                                                  Another approach is to to introduce the equivalent of a local name with what is known as a using declaration. The function f() from the previous example could be written as

                                                                                                                   void f2() throw()
                                                                                                                   {
                                                                                                                     using People::fred;      // Use People's fred
                                                                                                                     int i = fred + People::wilma;
                                                                                                                   }
                                                                                                                   

                                                                                                                  Another approach is called a using directive. Using this idea, the function f() from the previous example could be written as

                                                                                                                   void f3() throw()
                                                                                                                   {
                                                                                                                     using namespace People;   // Declare all names from People
                                                                                                                     int i = fred + wilma;
                                                                                                                   }
                                                                                                                   

                                                                                                                  Note that a using directive does not declare any variables; it merely makes names available.

                                                                                                                  Finally, notice that the global namespace can be thought of as the namespace without an identifier (i.e., ::fred refers to fred in the global namespace).

                                                                                                                    FAQ 15.04 Что произойдет , если 2 namespaces имеют одно имя ?

                                                                                                                    Suppose two different namespaces have their own versions of class string. Both namespaces can be introduced into the same code fragment without conflict, as long as there are no unqualified references to string. However, if there are unqualified references to string, the compiler issues an error because the unqualified name string is ambiguous. This is illustrated in the following example.

                                                                                                                     namespace A {
                                                                                                                       int x = 1;
                                                                                                                       int z = 2;
                                                                                                                     }
                                                                                                                     
                                                                                                                     namespace B {
                                                                                                                       int y = 3;
                                                                                                                       int z = 4;
                                                                                                                     }
                                                                                                                     
                                                                                                                     void doSomethingWith(int i) throw();
                                                                                                                     void sample() throw()
                                                                                                                     {
                                                                                                                       using namespace A;                                 <-- 1
                                                                                                                       using namespace B;                                 <-- 2
                                                                                                                     
                                                                                                                       doSomethingWith( x );                              <-- 3
                                                                                                                       doSomethingWith( y );                              <-- 4
                                                                                                                     
                                                                                                                       doSomethingWith( A::z );                           <-- 5
                                                                                                                       doSomethingWith( B::z );                           <-- 6
                                                                                                                     
                                                                                                                       #ifdef GENERATE_ERROR
                                                                                                                         doSomethingWith( z );                            <-- 7
                                                                                                                       #endif
                                                                                                                     
                                                                                                                     }
                                                                                                                     main()
                                                                                                                     { sample(); }
                                                                                                                     

                                                                                                                    (1) OK: Introduces A::x and A::z

                                                                                                                    (2) OK: Introduces B::y and B::z

                                                                                                                    (3) OK: Unqualified x unambiguously resolves to A::x

                                                                                                                    (4) OK: Unqualified y unambiguously resolves to B::y

                                                                                                                    (5) OK: The A:: qualifications makes this unambiguous

                                                                                                                    (6) OK: The B:: qualifications makes this unambiguous

                                                                                                                    (7) Error: Ambiguous: A::z or B::z?

                                                                                                                      FAQ 15.05 Правила использования namespaces?

                                                                                                                      The following example due to Stroustrup (C++ Programming Language, Third Edition, 1997) illustrates some of the basic ideas.

                                                                                                                       namespace X {
                                                                                                                         int i, j, k;
                                                                                                                       }
                                                                                                                       
                                                                                                                       int k;
                                                                                                                       
                                                                                                                       void f1() throw()
                                                                                                                       {
                                                                                                                         int i = 0;
                                                                                                                         using namespace X;  // make names from X accessible
                                                                                                                         i++;                // local i
                                                                                                                         j++;                // X::j
                                                                                                                         k++;                // error: X::k or global k?
                                                                                                                         ::k++;              // the global k
                                                                                                                         X::k++;             // X's k
                                                                                                                       }
                                                                                                                       void f2() throw()
                                                                                                                       {
                                                                                                                         int i = 0;
                                                                                                                         using X::i;         // Error: i declared twice in f2()
                                                                                                                         using X::j;
                                                                                                                         using X::k;         // Hides global k
                                                                                                                         i++;
                                                                                                                         j++;                // X::j
                                                                                                                         k++;                // X::k
                                                                                                                       }
                                                                                                                       

                                                                                                                        FAQ 15.06 Что такое name lookup?

                                                                                                                        Name lookup, sometimes known as Koenig lookup, is a clever solution to a difficult problem. Consider the problem of printing a string in the following example.

                                                                                                                         #include <iostream>
                                                                                                                         #include <string>
                                                                                                                         
                                                                                                                         void sample() throw()
                                                                                                                         {
                                                                                                                           std::string s = "hello world";
                                                                                                                           std::cout << s;                                    <-- 1
                                                                                                                         }
                                                                                                                         

                                                                                                                        (1) How does the compiler find the proper <<?

                                                                                                                        The operator<< that's needed is a non-member function that takes a string as a parameter and is packaged with the string class. How is the compiler supposed to find the proper operator? One approach is to make all the names in the entire standard namespace accessible without qualification (e.g., using namespace std;), but this is an ugly solution that is discussed in FAQ 15.09. An equally ugly alternative is to change sample() to the following:

                                                                                                                         void sample2() throw()
                                                                                                                         {
                                                                                                                         
                                                                                                                           std::string s = "hello world";
                                                                                                                           std::operator<< (std::cout, s);                    <-- 1
                                                                                                                         }
                                                                                                                         

                                                                                                                        (1) Ugly

                                                                                                                        Koenig lookup is a better alternative. It recognizes that non-member functions associated with a parameter class such as string can be thought of as part of an extended public: interface, and there is no harm in having the compiler automatically look for such functions in the same namespace as the parameter class. This is the technique known as name lookup, and it allows the example above to work "as is." The trick is that a class parameter, in this case string, somehow identifies a namespace, in this case std, and then all the non-member functions that refer to that parameter class are automatically made available. It's a pretty slick idea that usually simplifies life.

                                                                                                                          FAQ 15.07 Какие правила для испльзования имен namespace?

                                                                                                                          Relying solely on the scope operator leads to programs that are hard to read, particularly for frequently used classes such as string. Clutter is not desirable in programs that have to be maintained.

                                                                                                                          The using declaration is probably the best solution for most cases, because the developer specifically declares what is intended. But this can be tedious, particularly when pulling names from the standard library.

                                                                                                                          The using directive is less desirable than the using declaration because it pollutes the global namespace unnecessarily and opens the door for later code breakage. This is because compiler writers and class library providers can add names to the standard namespace, which means that programs that utilize using namespace std; can suddenly fail to compile even though the user has not changed anything.

                                                                                                                          Despite this, we utilize using namespace std; throughout this book to allow our examples to compile and to allow the reader who has not read this chapter yet to muddle through, but this practice has little to recommend it.

                                                                                                                            FAQ 15.09 В чем преимущества namespaces ?

                                                                                                                            Namespaces have cleaned up some old problems in two other ways. First, unnamed namespaces are preferable to the use of static global members and reduce the number of meanings that can be placed on the word "static." Second, using declarations are now the preferred alternative to access declarations that were used to work around some knotty problems with private inheritance.

                                                                                                                              FAQ 15.10 Как в namespaces решается проблема длинных имен ?

                                                                                                                              There is a simple macro-like solution for the long namespace name problem

                                                                                                                               namespace CWLN = CompanyWithLongName;
                                                                                                                               

                                                                                                                              which allows the use of either the short or the long form of the name. This solution is known as namespace aliases.

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

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

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