tips

 

C++

 

TODAY'S TIP: RENAMING A FILE
To rename a file, use the standard function rename() (declared in
<stdio.h>). This function takes two arguments of type const char
*--the first is the old name and the second is the new name of the
file:

include < stdio.h >
int main()
{
int stat=rename("backup.dat", "backup.old");
if (stat)
{
/*..rename failed*/
}
}

On success, rename() returns zero; otherwise, it returns a code
indicating the error type.


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

TODAY'S TIP: EQUIVALENCE IN STL
STL containers and algorithms require that you overload operator < for
the class type they store as elements. This is necessary for sorting
and comparing these elements. STL synthesizes the equality operator
from the < operator by using the following construct:

bool operator==( const T & arg1, const T & arg2)
{
return (!(arg1 < arg2)) && (!(arg1 < arg2))
}

In other words, you don't need to define an overloaded version of operator == (or any other relational operator) because STL knows how to generate these operators from operator <. Therefore, you only need to define an overloaded version of operator < for user-defined types.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

TODAY'S TIP: SWAPPING TWO INTEGERS WITHOUT USING A TEMPORARY
In a previous tip, I showed you how to swap two variables without using a temporary variable. There's another version of this solution that applies to integral values only. Here it is:

void swap(int& i, int& j)
{
i ^= j;
j ^= i;
i ^= j;
}/*i and j are swapped*/

You can apply this technique to any integral type--such as char, short, unsigned long--but not to floating-point variables. Note that the classic implementation of swap:

temp = i;
i = j;
j=temp;

is still the most efficient one. It's also more generic because it can be used with any type that supports assignment.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

TODAY'S TIP: COUNTING "LIVE" INSTANCES OF A CLASS
Counting the number of instances of a certain class is often necessary in debugging and performance tuning. To do that, declare a static data member as a counter, and have the constructor and copy constructor increment it and the destructor decrement it. Finally, add a static member function that returns the counter's value:

class A
{
private:
static int count;
public:
A() { ++count;}
A(const A& rhs) { ++count;}
~A() {--count;}
public:
static int get_count() {return count;}
};

int A::count; /*static member definition*/

Because static members are automatically zero-initialized, you can call this function and get the correct results even if no instance of
that class exists:

int main()
{
int n = A::get_count(); // 0
A a;
n = A::get_count(); // 1
A * p = new A(a);
n = A::get_count(); // 2
delete p;
n = A::get_count(); // 1
}

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

TODAY'S TIP: STACK UNWINDING IN THE EVENT OF AN UNCAUGHT EXCEPTION
When an exception is thrown and no matching handler can be found for it, C++ calls the function terminate(). By default, terminate() invokes the function abort(). Some compilers guarantee that at this point, the stack has been unwound, meaning all local automatic objects have been fully destructed, streams have been flushed, and open files
have been closed. Other compilers don't unwind the stack in this case, though. Whether the stack is unwound in the case of an uncaught exception is platform-defined. Therefore, you should check your compiler's documentation to know how it behaves in the event of an uncaught exception.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

TODAY'S TIP: REDUCING COMPILATION TIME
Generally speaking, the orthodox approach of dedicating a separate .cpp and .h file for every class is the way to go. However, in real-world programming you soon find yourself recompiling every app for hours just because you added another data member to a class. Here are two techniques for reducing build time.

First, group several related classes in a single .h file. Similarly, implement these classes in a single .cpp file. Two derived classes can usually be considered related--that is, a family of exception classes. Likewise, classes that depend on one another or classes that
participate in a single task are related classes. Declaring such classes in a single .h file and implementing them in a single .cpp file reduces the overall build time and simplifies maintenance. Note that with every #include directive, time is wasted on accessing the
physical file, opening it, and reading its content. Thus, by reducing the number of separate physical files you also reduce the total build time.

Several compilers support incremental compilation and link features, precompiled and cached headers. Enabling these features also reduces build time considerably. Consult your compiler's manual for further information.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

TODAY'S TIP: FLOATING-POINT NUMBERS REPRESENTATION
People sometimes complain about the inaccuracy of floating-point arithmetic. To demonstrate the level of floating-point inaccuracy, consider the following program:

#include < iostream >
using namespace std;
int main()
{
float f1 = 2000.58;
float f2 = 2000.0;
cout << f1 - f2;
}

On my machine, this program prints 0.579956 instead of 0.58. More complex calculations yield higher inaccuracy. What is going on here?

Remember that rounding, approximation, and truncation are not the responsibility of C++. Rather, they depend on the particular hardware your machine uses. Furthermore, floating-point numbers are merely an approximation based on the IEEE standard.

On most machines, type float occupies 32 bits, of which 1 bit will be used for the sign representation, 8 bits for exponent, and the remaining 23 bits for the mantissa. Because a mantissa always has the form 1.nnnn... the leading 1 can be dropped so there are actually 24 bits allocated for the mantissa. 24-bit accuracy can have a deviation of %0.0000062 from the original number. For higher accuracy, you can use the type double, which provides 53 bits of mantissa. This is more accurate than 24 bits, but you can never get absolute accuracy with bits.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

TODAY'S TIP: PASSING ARGUMENTS WITH SIDE EFFECTS
On many implementations, the ANSI C library functions are in fact macros in disguise. memset(), memcpy(), strcpy(), and others are often implemented as macros that invoke low-level system routines. While Standard C++ strictly forbids this macro-in-disguise trick, it is still widely used in C. Beware of passing arguments with a side effect to such pseudo-functions. The results in this case might be undefined. To see the potential dangers of passing an argument with a side effect to a macro in disguise, consider this example:

char buff[12];
int n = 0;
/*second argument has a side-effect; bad idea*/
memset(buff, ++n, sizeof(buff));

If memset() happens to be a macro that passes its arguments to another function, n's value is incremented twice before its usage.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

TODAY'S TIP: DYNAMIC TYPE AND STATIC TYPE
The type of the most derived object to which an expression refers is said to be the dynamic type of that expression. For example, if p is declared as a pointer to class B but it actually points to an object of class D (where D is derived from B), the dynamic type of the expression *p is D whereas its static type is B:

B * p; /*static type of *p is B*/
p = new B;/*dynamic type of *p is D*/

The same is true for references:

void f(B & b); /* b's static type is B&*/
D d;
f(d); /*argument's dynamic type is D&*/


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

TODAY'S TIP: DIFFERENCES PROTOTYPES OF STANDARD FUNCTIONS
A small number of functions from the Standard Library have different signatures in C and C++. These functions are: strchr(), strpbrk(), strrchr(), strstr(), and memchr() as well as their wide-character counterparts: wcschr(), wcspbrk(), wcsrchr(), wcsstr(), and wmemchr(). They are declared in the standard header <string.h> (or < cstring > in
C++). Let's look at strstr(). While in C, strstr() has the following prototype:

char* strstr(const char* s1, const char* s2); /*ANSI C*/

In C++, this function has two different prototypes that are
incompatible with the C version:

char* strstr(char* s1, const char* s2); /*ANSI C++*/
const char * strstr(const char* s1, const char* s2); /*ANSI C++*/

Another example: the function strpbrk(). In C, it has this signature:

char* strpbrk(const char* s1, const char* s2); /*ANSI C*/

In C++, it has two different signatures:

char* strpbrk(char* s1, const char* s2); /*ANSI C++*/
const char* strpbrk(const char* s1, const char* s2); /*ANSI C++*/

Can you see the pattern here? The C function takes "const X *" and returns "X *" whereas C++ defines two functions, one taking "X *" and returning "X *", and another taking "const X *" and returning "const X *". Pay attention to these subtle differences when you port code from C to C++ or vice versa.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


TODAY'S TIP: CONSTANT EXPRESSIONS
C++ requires the use of integral constant expressions in various
contexts; for example, when you define arrays' sizes, case labels,
bit-field sizes, and enumerator initializers. An integral constant
expression is one of the following:

* A numeric literal, as 100 in

x < 100;

* Enumerators such as up and down in the following enum type:

enum state {up, down};

* Const variables or static data members of integral or enumeration
types initialized with constant expressions--for example, x in this
definition:

const int x = 0;

* Non-type template parameters of integral or enumeration types:

S < 100 > s;

* Finally, a sizeof expression:

sizeof (int);

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

TODAY'S TIP: RECURSION: MYTH AND REALITY
CS sophomores learn that recursion can neatly solve complex problems such as wildcards handling and code parsing. In practice, though, recursion often complicates matters unnecessarily. Every recursion-based algorithm can be expressed as an iterative one.
Iteration is more common and intuitive to most programmers than recursion. Additionally, it's more efficient because it avoids the overhead of recurrent function calls (recursive calls usually aren't inlined, either). Furthermore, while you can have a loop that executes infinitely, every operating system limits the number of possible recursive calls. The limit depends on available stack memory. Thus, the behavior of programs with many recursive calls might be unpredictable if the depth of recursion exceeds that stack's limit.

Recursion is cute and can impress your college instructor. However, in real-world programming, you should use it sparingly.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

TODAY'S TIP: TYPE QUALIFIERS' ORDER
Do the following declarations differ in any way from each other?

const long int N=0;
long const int N=0;

No, they don't. C++ doesn't enforce a specific order of type qualifiers in a declaration. Therefore, you can use any permutation without changing the declaration's semantics. In both cases, the compiler builds the same internal tree and produces the same assembly
code and mangled names for functions.


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

TODAY'S TIP: COMPUTING THE MAXIMUM OF THREE VALUES
The std::max() template takes two values and returns the highest. What if you need to calculate the maximum of three values? Don't write a special function template for this purpose. Instead, apply max() to two arbitrary values of the three, and then apply max() once again to the result and the third value:

int l=10, m=11, n=7;
int highest = max(l,max(n,m));
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 

Privacy Policy / ContactUs / HomePage
Copyright acontractorsworld.com ©, All rights reserved.