[34] Miscellaneous technical issues
(Part of C++ FAQ Lite, Copyright © 1991-99, Marshall Cline, cline@parashift.com)


FAQs in section [34]:


[34.1] Why can't the compiler find my header file in #include "c:\test.hpp" ?

Because "\t" is a tab character.

You should use forward slashes ("/") rather than backslashes ("\") in your #include filenames, even on an operating system that uses backslashes such as DOS, Windows, OS/2, etc. For example:

    #if 1
      #include "/version/next/alpha/beta/test.hpp"    
// RIGHT!
    #else
      #include "\version\next\alpha\beta\test.hpp"    
// WRONG!
    #endif

Note that you should use forward slashes ("/") on all your filenames, not just on your #include files.

TopBottomPrevious sectionNext section ]


[34.2] Does C++ have new scoping rules for for loops? UPDATED!

[Recently wordsmithing changes thanks to Lance Ware (on 7/99). Click here to go to the next FAQ in the "chain" of recent changes.]

Yep.

The following code used to be legal, but not any more, since i's scope is now inside the for loop only:

    for (int i = 0; i < 10; ++i) {
      
// ...
      if ( 
/* something weird */ )
        break;
      
// ...
    }
    
    if (i != 10) {
      
// We exited the loop early; handle this situation separately
      
// ...
    }

Unless you use a for loop variable after the for loop, the new scoping rule won't break your code. If it does break your code, in most cases the compiler will give you a compile-time error message such as "Variable i is not in scope".

Unfortunately it is possible that this new rule will silently cause your code to do the wrong thing. For example, if you have a global variable i, the above code if (i != 10) silently change in meaning from the for loop variable i under the old rule to the global variable i under the new rule. This is not good. If you're concerned, you should check with your compiler to see if it has some option that forces it to use the old rules with your old code.

Note: You should avoid having the same variable name in nested scopes, such as a global i and a local i. In fact, you should avoid globals althogether whenever you can. If you abided by these coding standards in your old code, you won't be hurt by the new scoping rules for for loop variables.

TopBottomPrevious sectionNext section ]


[34.3] Why can't I overload a function by its return type?

If you declare both char f() and float f(), the compiler gives you an error message, since calling simply f() would be ambiguous.

TopBottomPrevious sectionNext section ]


[34.4] What is "persistence"? What is a "persistent object"?

A persistent object can live after the program which created it has stopped. Persistent objects can even outlive different versions of the creating program, can outlive the disk system, the operating system, or even the hardware on which the OS was running when they were created.

The challenge with persistent objects is to effectively store their member function code out on secondary storage along with their data bits (and the data bits and member function code of all member objects, and of all their member objects and base classes, etc). This is non-trivial when you have to do it yourself. In C++, you have to do it yourself. C++/OO databases can help hide the mechanism for all this.

TopBottomPrevious sectionNext section ]


[34.5] Why is floating point so inaccurate? Why doesn't this print 0.43? UPDATED!

[Recently added return type to main() (on 10/99). Click here to go to the next FAQ in the "chain" of recent changes.]

    #include <iostream.h> 
    
    int main()
    {
      float a = 1000.43;
      float b = 1000.0;
      cout << a - b << '\n';
    } 

(On one C++ implementation, this prints 0.429993)

Disclaimer: Frustration with rounding/truncation/approximation isn't really a C++ issue; it's a computer science issue. However, people keep asking about it on comp.lang.c++, so what follows is a nominal answer.

Answer: Floating point is an approximation. The IEEE standard for 32 bit float supports 1 bit of sign, 8 bits of exponent, and 23 bits of mantissa. Since a normalized binary-point mantissa always has the form 1.xxxxx... the leading 1 is dropped and you get effectively 24 bits of mantissa. The number 1000.43 (and many, many others) is not exactly representable in float or double format. 1000.43 is actually represented as the following bitpattern (the "s" shows the position of the sign bit, the "e"s show the positions of the exponent bits, and the "m"s show the positions of the mantissa bits):

        seeeeeeeemmmmmmmmmmmmmmmmmmmmmmm 
        01000100011110100001101110000101 

The shifted mantissa is 1111101000.01101110000101 or 1000 + 7045/16384. The fractional part is 0.429992675781. With 24 bits of mantissa you only get about 1 part in 16M of precision for float. The double type provides more precision (53 bits of mantissa).

TopBottomPrevious sectionNext section ]


[34.6] How can I create two classes that both know about each other? NEW!

[Recently created thanks to Steve Horne (on 10/99). Click here to go to the next FAQ in the "chain" of recent changes.]

Use a forward declaration.

Sometimes you must create two classes that use each other. This is called a circular dependency. For example:

    class Fred {
    public:
      Barney* foo();  
// Error: Unknown symbol 'Barney'
    };
    
    class Barney {
    public:
      Fred* bar();
    };

The Fred class has a member function that returns a Barney*, and the Barney class has a member function that returns a Fred. You may inform the compiler about the existence of a class or structure by using a "forward declaration":

    class Barney;

This line must appear before the declaration of class Fred. It simply informs the compiler that the name Barney is a class, and further it is a promise to the compiler that you will eventually supply a complete definition of that class.

TopBottomPrevious sectionNext section ]


[34.7] What special considerations are needed when forward declarations are used with member objects? NEW!

[Recently created thanks to Steve Horne (on 10/99). Click here to go to the next FAQ in the "chain" of recent changes.]

The order of class declarations is critical.

The compiler will give you a compile-time error if the first class contains an object (as opposed to a pointer to an object) of the second class. For example,

    class Fred;  // Okay: forward declaration
    
    class Barney {
      Fred x;  
// Error: The declaration of Fred is incomplete
    };
    
    class Fred {
      Barney* y;
    };

One way to solve this problem is to reverse order of the classes so the "used" class is defined before the class that uses it:

    class Barney;  // Okay: forward declaration
    
    class Fred {
      Barney* y;  
// Okay: the first can point to an object of the second
    };
    
    class Barney {
      Fred x;  
// Okay: the second can have an object of the first
    };

Note that it is never legal for each class to fully contain an object of the other class since that would imply infinitely large objects. In other words, if an instance of Fred contains a Barney (as opposed to a Barney*), and a Barney contains a Fred (as opposed to a Fred*), the compiler will give you an error.

TopBottomPrevious sectionNext section ]


[34.8] What special considerations are needed when forward declarations are used with inline functions? NEW!

[Recently created thanks to Steve Horne (on 10/99). Click here to go to the next FAQ in the "chain" of recent changes.]

The order of class declarations is critical.

The compiler will give you a compile-time error if the first class contains an inline function that invokes a member function of the second class. For example,

    class Fred;  // Okay: forward declaration
    
    class Barney {
    public:
      void method()
      {
        x->yabbaDabbaDo();  
// Error: Fred used before it was defined
      }
    private:
      Fred* x;  
// Okay: the first can point to an object of the second
    };
    
    class Fred {
    public:
      void yabbaDabbaDo();
    private:
      Barney* y;
    };

One way to solve this problem is to move the offending member function into the Barney.cpp file as a non-inline member function. Another way to solve this problem is to reverse order of the classes so the "used" class is defined before the class that uses it:

    class Barney;  // Okay: forward declaration
    
    class Fred {
    public:
      void yabbaDabbaDo();
    private:
      Barney* y;  
// Okay: the first can point to an object of the second
    };
    
    class Barney {
    public:
      void method()
      {
        x->yabbaDabbaDo();  
// Okay: Fred is fully defined at this point
      }
    private:
      Fred* x;
    };

Just remember this: Whenever you use forward declaration, you can use only that symbol; you may not do anything that requires knowledge of the forward-declared class. Specifically you may not access any members of the second class.

TopBottomPrevious sectionNext section ]


[34.9] Why can't I put a forward-declared class in a vector<>? NEW!

[Recently created thanks to Steve Horne (on 10/99). Click here to go to the next FAQ in the "chain" of recent changes.]

Because the vector<> template needs to know the sizeof() its contained elements, plus the vector<> probably accesses members of the contained elements (such as the copy constructor, the destructor, etc.). For example,

    class Fred;  // Okay: forward declaration
    
    class Barney {
      vector<Fred> x;  
// Error: the declaration of Fred is incomplete
    };
    
    class Fred {
      Barney* y;
    };

One solution to this problem is to change Barney so it uses a vector<> of Fred pointers rather than a vector<> of Fred objects:

    class Fred;  // Okay: forward declaration
    
    class Barney {
      vector<Fred*> x;  
// Okay: Barney can use Fred pointers
    };
    
    class Fred {
      Barney* y;
    };

Another solution to this problem is to reverse the order of the classes so Fred is defined before Barney:

    class Barney;  // Okay: forward declaration
    
    class Fred {
      Barney* y;  
// Okay: the first can point to an object of the second
    };
    
    class Barney {
      vector<Fred> x;  
// Okay: Fred is fully defined at this point
    };

Just remember this: Whenever you use a class as a template parameter, the declaration of that class must be complete and not simply forward declared.

TopBottomPrevious sectionNext section ]


E-Mail E-mail the author
C++ FAQ LiteTable of contentsSubject indexAbout the author©Download your own copy ]
Revised Oct 15, 1999