Search
 
 

Display results as :
 


Rechercher Advanced Search

Latest topics
» ~=?{The future is here, TastyExploit officially open to the public}?=~
Tue Mar 02, 2010 11:00 pm by Dami

» My first aa script
Tue Mar 02, 2010 7:00 am by wafflemaster

» the real story behind Maverin
Tue Mar 02, 2010 1:33 am by SoundOfDeat

» Random spam
Mon Mar 01, 2010 11:52 pm by Danny1994

» [Request] EMS v56 Exe
Mon Mar 01, 2010 12:39 am by Dami

» [Blacklist] NX-Trading Blacklist.
Mon Mar 01, 2010 12:38 am by Danny1994

» I have a question regarding the meso farmer
Sat Feb 27, 2010 10:30 pm by Dami

» What are you listening to now?
Sat Feb 27, 2010 7:57 pm by Noname111111

» Video(s) Of The Day
Sat Feb 27, 2010 7:37 pm by Noname111111

Navigation
 Portal
 Index
 Memberlist
 Profile
 FAQ
 Search
Affiliates
free forum
 



[C++] More Tips

Go down

[C++] More Tips

Post by Dami on Fri Oct 30, 2009 4:20 am

C++ tips

This is a short list of recommendations on how to use C++. My experiences are from using gcc 2.8.0 and Visual C++ 5.0. I had to have things compatible between these two compilers, and between Unix and Windows. Of course, most of these tips are in Bjarne Stroustup's book in some form.
Contents

Compilers

IO of binary files

When are destructors called for local variables

Use {} to keep things local

Scope of variables declared in for()

When to use virtual members


Compilers

* For gcc, please upgrade to 2.8.1 or later. (If you need namespaces, go for 2.95.1 or later). Template support is much better now. However, versions 2.8.0 and 2.8.1 (and also egcs 1.0.2) have a bug when using new to allocate an array, and specifying arguments for the constructors, i.e. new A[4](arg1, arg2). If you use 2 arguments, g++ looks for a constructor with 1 argument. When such a constructor exists, it calls it (with as parameter 'arg1').
Here is a piece of code which illustrates it, it doesn't contains a 1 arg constructor, so the code does not compile.
Code:

class A
{
public:
int a; int b; A(int va, int vb) : a(va), b(vb)
{
}
}; main()
{
A* pa = new A[1](4,5);
}


* For Visual C++ 5.0, download Service Pack 3. ALWAYS use C++ system include files without the .h suffix. For instance,
Code:

#include

Mixing (for instance) fstream.h and fstream will give funny errors (mostly related to strings).

Back to contents
IO of binary files

To make sure that there is no CR/LF translation on non-Unix computers, you have to use the following lines to open streams to files with binary data.
Code:

ofstream os("output.flt", ios:ut | ios::binary);
ifstream is("output.flt", ios::in | ios::binary);
For Visual C++, when using fstream.h, use in addition the flag ios::nocreate. Otherwise you can open a non-existing file for reading, without complaining. (This is not necessary when using fstream).

Back to contents
When are destructors called for local variables

Non-static (or 'automatic' ) variables are 'destructed' automatically when they go out of scope. Scope is a farily complicated thing, and I'm not going to repeat the definition here. Roughly speaking the scope ends when you encounter the } around the declaration of the variable. See also the use of {} and how scope is defined in the for() statement.

Variables are destructed (by the compiler) by calling the appropriate destructor of their class. If the objects allocate memory (and hence the destructor should free that memory), this means that you recover the memory allocated.
Code:

class array
{
private:
float *ptr;
public:
// constructor
array(int n) { ptr = new float[n]; }
// destructor
~array() { delete [] ptr; }
}

main()
{
// ...
{
array a(5); // allocates memory
// do something
}
// here the array is destructed, and so the memory is freed
// ...
}

Back to contents
Use {} to keep things local

Use of the grouping construct {} enables you to declare variables local to that group. When leaving the group, all local variables are destructed. This has the advantage that the reader of the code knows (s)he shouldn't worry about these variables to understand the rest of the code.
In a way this can be understood as if every use of {} is like a function call (with local variables declared in the function). Of course, you don't have the overhead of stack manipulations and jumps involved in a proper function call.
Code:

// recommended usage
void f(int a)
{
if(a==1)
{
myclassA Aobject;
// here I do something with 'Aobject', and maybe 'b'
}
// Aobject does not exist here anymore
}

This tip is just an extension of the 'avoid global variables' credo.

As always, this can be disabused as in the following piece of code, where the outer variable 'a' is hidden by a local 'a', resulting in not very readable code.
Code:

// not very readable code
{
int a=1;
{
// local variable hides outer 'a'
int a;
a = 2;
assert (a==2);
}
// a is again the previous variable
assert (a==1);
}

Back to contents
Scope of variables declared in for()

The new ANSI C++ standard specifies that variables declared as in for(int i=1; ...) have a scope local to the for statement. Unfortunately, older compilers (for instance Visual C++ 5.0) use the older concept that the scope is the enclosing group. Below I list 2 possible problems, and their recommended solutions:

* you want to use the variable after the for() statement

you have to declare the variable outside of the for() statement.
Code:

int i;
for(i=1; i<5; i++)
{ /* do something */ }
if (i==5) ...
* you want to have multiple for() loops with the same variables.

Put the for statement in its own group. You could also declare the variable outside of the 'for', but it makes it slightly trickier for an optimising compiler (and a human) to know what you intend.
Code:

{
for(i=1; i<5; i++)
{ /* do something */ }
}

Back to contents
When to use virtual members

Make a member 'virtual' if a derived class extends the functionality of a member of the base class, and this extended functionality has to be accessible:

1. inside other member functions of the base class
2. when using pointers that can point to either an object of the base class, or an object of the derived class.

Example: multi-dimensional arrays which are defined recursively in terms of a 1D array.
We wanted to have a 'grow' member that enlarged the outer dimension of the multidimensional array. At first sight, this is simply calling a general grow of the base class. However, 'grow' has to know the size of the new elements (which are again multidimensional arrays). So, we had to define in the derived class a new 'grow', which calls the base class 'grow' first, and then does more stuff.
At many points in the base class, 'grow' is called to adjust sizes. By making 'grow' virtual we avoid to having to rewrite these members for the derived class.

Caveat:

For members of the base class which use temporary objects of its own type, the base class 'grow' will be called. For instance:
Code:

class array
{
...
virtual void grow(int new_size);

array& operator +=( const array& a)
{ /* some definition using 'grow' */ }
array operator +(const array& a1, const array& a2)
{
array a=a1;
a += a2;
// Warning, this will call array::grow, even if a1 is really from a derived type
}
};

Thus, you should provide a member of the derived class for every member of the base class which uses temporary objects.
Code:

class multiarray : public array
{
...
virtual void grow(int new_size);

multiarray operator +(const multiarray& a1, const multiarray& a2)
{
multiarray a=a1;
a += a2;
}
};
avatar
Dami
Dragon
Dragon

Posts : 1414
Join date : 2009-08-10
Age : 29
Location : Finland

Back to top Go down

Back to top

- Similar topics

 
Permissions in this forum:
You cannot reply to topics in this forum