Hello Guest, please login or register.
Did you miss your activation email?
Login with username, password and session length.

Pages: [1]   Go Down

Author Topic: old tutorial on referencing  (Read 959 times)

0 Members and 1 Guest are viewing this topic.

aab

^ Evolved from a Hobbit
old tutorial on referencing
« on: September 18, 2007, 03:51:00 pm »
  • *
  • Reputation: +0/-0
  • Offline Offline
  • Gender: Male
  • Posts: 992

Old one that i did a while ago; Couldnt find it ona  search but someone asked about it so here it is again.



A simple tutorial on using an alias for a variable.

This is quite an early tutorial, however the last part 'mentions' security and efficiency of a class, and requires many months of phsycological C++'ing to go through completely.

There is ALOT to read through, but its seperated into chunks so if you only want to hear of a chunk at a time ok.
Ok..so the chunks each have alot to read through themselves... But it should read quickly as i analyse and explain in the detail that exists...
Some people say in detail, yes, but when i say the detail that exists... i mean everything.

Anyway, the chunks read fast, and theres no summary as the point is...after you read through this, you will never forget lol.





You may have already come accross the method of aliasing variables before realising other uses of it, in the form of a referenced parameter.

For that reason I'll refer to them as references (Also because my grammers horrible and im not sure how to use the word 'alias' properly).


Referencing Parameters (Most of you can read past this part)

   Assuming you can create a function, and pass parameters to a function:


   When you pass parameters to a function, you are giving it a value that it can use to vary what it does, and how it does it.
   Inside that function, there can be if statements and arithmetic operations which vary the results of the operations in that function:
   
   This is neat as you can use that function many times, and each time it will act slightly differently depending on how you want it to.

   So, eg you could make a function NewLines, that prints a new line the number of times you ask it to:
   
Code: [Select]
#include<iosteam>

void NewLines ( int numNewLines )
{
for( int t=0; t<numNewLines ; ++t )
{
std::cout<<std::endl;
}
}

int main()
{
using namespace std;

int numberOfGaps = 5;

cout<< "start" ;
NewLines ( numberOfGaps ) ; //print 5 new lines
cout<< "end" ;

cin.get();
return 0;
}

      The parameter in the function (eg: numNewLines) is a new variable, created inside that function, for its use only.
   When you call the function and pass a parameter (eg: NewLines( numberOfGaps ) ), it makes the new variable in the function, and makes its value equal to the variable you pass, at the very start of the function.
   /*Strictly, It doesnt eg go 'int numNewLines=numberOfGaps;' when the function is called, however for standard variables (int,float,double etc) the effect of what it does do is the same.*/
   However, because the parameter in the function was only made equal to the variable passed to it at the start of the function, any changes to the variable inside the function, arent going to affect the variable outside the function. The functions variable only copies the value of the passed variable: They are not the same.

   But what if you wanted to get some value back from the function?
   Well you can return ( assuming you know what that is: If you dont you dont
   need to to understand this section of this tutorial ).
   Unless you return some sort of object, or a value with a meaning to you (eg: if(1) doA() else if(2) doB() else.. ) your limited here, until you learn that you can reference parameters.

   This is where references are usefull:
   Referenced parameters are actually the same as the variable that is passed into the function:
   They may have a different name, but as long as they are the same type, its compiles.
   Therefore changes to the variable inside the function, are changes to the variable outside the function.
   Thus, this variable can be called an 'alias' (Same thing, but different name);

   This type of parameter can also be called a 'reference', because it 'references the location of the parameter passed to the function', wheras a normal parameter (eg: int numNewLines) creates a new variable in a new location, and copies the value from the passed parameters location into its location.

   The syntax for a referencing parameter is to put an ampersand inbetween the typename and the variables identifier.
   eg:
   
Code: [Select]
void MyFunction ( int & parameter )
{
// [b]. . .[/b]
}
   The '&' symbol, when placed infront of a variable (not inbetween two variables) means 'address of'.
   Lets look at that again, with 'address of' in place, to see how much that makes sense: void MyFunction ( int address of parameter )
   
   If youve used pointers
   {
      Youll see this healthily makes the same sense as it does when assinging a pointer to a variable:

         int x;
         int*px = &x; // px = address of x;

      In addition, note the reverse of using '*'.

      '*' infront of a variable (not inbetween two variables) means 'whats at the address'.
      Such that you can (continuing from above) go:
      
         int y = *px;   // y = 'whats at the address' px  // same result as going y = x;

      And when declaring pointers:

         int 'whats at the address' pointer;
      
      See?.Pointer syntax is incredibly sensible.

      If you have used pointers, youll already know that you could use pointers to do the above, eg:
      
Code: [Select]
void MyFunction ( int * aparameter )
{
*aparameter = 34;
// [b]. . .[/b]
}
      But what if someone calls this like so: MyFunction(0)
      Well, the best possible thing that could happen is the program crashing at runtime.
      You could add in a check that it wasnt 0...But still someone could pass any number
      in, C-cast to a pointer.
      So, by using references, the function is safer, easier to use, and easier to write.
   }

   So now, you can make a function that changes the values of its parameters.
   
   So for example:
   
Code: [Select]
#include<iostream>

void DontMakeFive( int x )
{
x = 5;
}

void MakeFive( int &x )
{
x = 5;
}

int main()
{
using namespace std;

int value = 32; // value is 32


DontMakeFive( value ); // wont do anything as the 'x' in that function is just a copy.

cout<< value ; // =>this will print out '32'


MakeFive( value ); // will change values value.

cout<< value ; // =>this will print out '5'


cin.get();
return 0;
}


   Something you cant do:

   You couldnt call the above like so:

   MakeFive( 45 );

   This is self explanitory of course...You cant make 45, 5.
   In particular, because 45, written into your program, is not a variable: Its a literal value embedded into your compiled code.
   Its not like you can go 45 = 5;





Variable Reference (within same function)

   A declared variable can actually be a reference, and be made '=' to another when declared. In doing this, the two variables are the same, but there are two names for this variable. Several languages dont allow this kind of thing. You can write utterly occluded code, eg:

      x = 5;
      y = 10;
      cout<< x; //prints 10, not 5. lol....How Evil.

   Thats the very minimal of the evil that you can accomplish in C++...But in programming, writing good code is more fun, so well think about how this is usefull.

   This referencing variable is created like so:

      int x;

      int &also_x = x;

      //      eg:
      x=5;
      cout<< also_x;

      This is an 'implicit' property: There are very many in most programming langauges.
      What i mean by 'implicit', is that the what you have written doesnt explicitly compile as it is (ie its the opposite of 'explicit').
   
         ie: the compiler will do things <to the copy of the code it makes for compilation>.
      
      You can think of the compiler implicitly replacing 'also_x' with 'x', and then removing your line of code 'int &also_x = x;' before it compiles to the final object code that can be run by the computer;
      

      The compiler will also do these kind of things for constants and enumerates eg:
         
Code: [Select]

int player_health;
const int max_health = 20;

/*
[b]. . . [/b]
*/

//make players health maximum:

player_health = max_health; //v will be replaced with 20


         You can see constants like that are usefull, as if you wanted to change max_health, you wouldnt have to go through every time you used it, and change it to the new literal value (eg 22 ).
         Its also alot more meaningfull.

         But why would you want to reference a variable?
         I mean...You already have the first one..Whats the point.


         Well..You could sometimes use it to make code thats more readable, or uses less variables

         For example, say you had a file, with someones age, then someones height (as ints), and wanted to print them out:
         //Just pretend ive int main, and #include< using namepacce etc:
         
Code: [Select]
int read_value;
ifstream file( "john.txt" );

file>>read_value;
cout<< "John is " <<read_value<< " Years old ";

file>>read_value;
cout<< "and is" << read_value << "cm tall";

file.close();
         Well, thats ok, but read_value is not a very meaningfull variable name. Ok so who cares in that tiny little snippet of code, anyone can read the next line and see what its being used for. But what about a massive program, with a great deal of reads and outs, and not much meaning around that code?
         Well:
         
Code: [Select]
int
johnsAge,
johnsHeight
;
ifstream file( "john.txt" );

file>>johnsAge;
cout<< "John is " <<johnsAge<< " Years old ";

file>>johnsHeight;
cout<< "and is" << johnsHeight << "cm tall";

file.close();
         hmm...I liked it better the other way as you only had one variable..Now there are two. Duh that thats not good.
         So..Reference anyone?
         
Code: [Select]
int read_value;
int
&johnsAge = read_value,
&johnsHeight= read_value
;
ifstream file( "john.txt" );

file>>johnsAge;
cout<< "John is " <<johnsAge<< " Years old ";

file>>johnsHeight;
cout<< "and is" << johnsHeight << "cm tall";

file.close();
         Of course, there still exists the possibilty of confusion by someone later on changing one and not realising the change on the other...
         So, you *could* use a confinement on the variables access space:
         
Code: [Select]
int read_value;

ifstream file( "john.txt" );

{
int &johnsAge = read_value;
file>>johnsAge;
cout<< "John is " <<johnsAge<< " Years old ";
}

{
int &johnsHeight= read_value;
file>>johnsHeight;
cout<< "and is" << johnsHeight << "cm tall";
}

file.close();
         A rather simple and slow example that was i suppose....But now lets think about it when weve a large program with lots of nested classes, and we want to do something like:
         
Code: [Select]
for(int t=0; t<GameEngine->GameArea->tiles[player->Body.Layer].num_tiles(); ++t)
{

if(GameEngine->GameArea->tiles[player->Body.Layer][t].CollType)
{


pnt.x = GameEngine->GameArea->tiles[player->Body.Layer][t].x;
pnt.y = GameEngine->GameArea->tiles[player->Body.Layer][t].y;

if( PtInRect(&CloseRect,pnt) )
{

ApplyCollision
(
GameEngine->GameArea->tiles[player->Body.Layer][t].CollType,
&Crect,
GameEngine->GameArea->tiles[player->Body.Layer][t].x,
GameEngine->GameArea->tiles[player->Body.Layer][t].y
);

//and so on.....
         //Pretend you dont know about efficient address iterating if you do.
         Well...thats repeating something...Hard to read..ugly.
         Lets do this instead:
         
Code: [Select]

MyTileGrid & tileList = GameEngine->GameArea->tiles[player->Body.Layer];

for(int t=0; t<tileList.size(); ++t)
{

if( tileGrid[t].CollType)
{


pnt.x = tileList[t].x;
pnt.y = tileList[t].y;

if( PtInRect(&CloseRect,pnt) )
{

ApplyCollision
(
tileList[t].CollType,
&Crect,
tileList[t].x,
tileList[t].y
);

//and so on.....
         Much better, wouldnt you agree?
         And ill bet some of you will look at the first one adn go: "Whait..maybe tommorw..or next year or something" But, if you saw the second version in an example, you'd be ok.


         Another thing to note: A reference must be initiated with a variable to reference.
         eg:
            int&x;
         Is illegal because x has no variable to alias...nothing to be replaced with.


References in a Class(Youll need to know more than the above for this stuff)

   A class can have a reference member.
   
   However, remmember that a reference must be initialised with a variable to reference.

   You must therefore pass a variable to the classes constructor, and this must be assigned to the reference member on its initialisation, which can only be done through the implicit constructor initialisation:
   If youve not done that yourself, youll have to find its explanation somewhere else for now.

   eg:
   
Code: [Select]
class my_class
{
int &reference_member;

public:

explicit //if you dont know why this is here : It stops the compiler using this constructor for casting an integer to my_class (eg you could go instanceOfMyClass = x; and it wouldnt mind.
my_class(int&to_be_referenced):
reference_member( to_be_referenced ) //'reference_membe' initialised with the variable 'to_be_referenced'
{
}
};

   There are situations where this can be usefull, and its again safer than using pointers.

   Now...Lets consider a class referncing its own members:

   
Code: [Select]
class my_class
{
int &reference_member;
int member_to_be_referenced ; //I dont support variable names this long but ..just an examplifying name.

public:

my_class():
reference_member( member_to_be_referenced ) //'reference_membe' initialised with the variable 'to_be_referenced'
{
}
};

   Not much point...
   There may be a little point in doing something like this i suppose:
   
Code: [Select]
struct color
{
char
samples[4],
&red,
&green,
&blue,
&alpha;

color():
red ( samples[0] ),
green ( samples[1] ),
blue ( samples[2] )
alpha ( samples[3] )
{
}
};
   This would allow someone to use this color class in various ways easily.



   But heres something nice and usefull.

   You know how it can be annoying..writing a get function for all these variables in your class, just so that is safer (people not being able to change data and such that would compromise the integrity of the class), just return a copy of the values they want to know:
   
Code: [Select]
class my_class
{
int a,b,c,d,e,f;
public:
inline int geta()const{ return a;};
inline int getb()const{ return b;};
inline int getc()const{ return c;};
inline int getd()const{ return d;};
inline int gete()const{ return e;};
inline int getf()const{ return f;};
};
   Now that not might be that ugly in that example, but if a,b,c,d,e,f are all different sizes, its a mess to read.
   Well...A public const member could be accessed from outsid the class, but wouldnt be allowed to be changed.

   So...What about a const reference to that member instead of a get function?
   Seems very sensible doesnt it?
   You want the person to access the values, but withought changing them, and thats exactly what it would do.
   It would replace the refernces name with the refferred, but not allow the user to change it.

   So, for example:
   
Code: [Select]
class my_class
{
int a,b,c,d,e,f;
public:
const int
&const_a,
&const_b,
&const_c,
&const_d,
&const_e,
&const_f;

my_class():
const_a( a ),
const_b( b ),
const_c( c ),
const_d( d ),
const_e( e ),
const_f( f )
{
}
};
   Does that not make you happier?

   You could always make it very small and simple, like:
   
Code: [Select]
class my_class
{
int a_,b_,c_,d_,e_,f_;
public:
const int &a,&b,&c,&d,&e,&f;
my_class():
a(a_),b(b_),c(c_),d(d_),e(e_),f(f_)
{
}
};

   However naming the members const_a is a good idea as it tells the user of the class that its const.
   geta would always be immediately thought of as a function...
   Ill leave the dilemma of how to name these things up to you.....

   Final thing to mention, is that to remmember to consider what happens when the default
   copy constructor and operator= are called.
   The references would be muddled up, referencing variables from the other class, so the compiler,
   in making sure this is recognised, doesnt create a default operator=() or copy constructor.
   Simply define them yourself, withought equating the refernces of course.




Oh and btw....This is all my own heuristic observation so feel free to comment. But i trust myself.
























Logged




I ♥ Sol
.... I ♥ Sol ? wtf how long has that been there? >_> *rrrrrrrrar*
  • MySpace
Pages: [1]   Go Up

 


Contact Us | Legal | Advertise Here
2013 © ZFGC, All Rights Reserved



Page created in 0.018 seconds with 39 queries.

anything