Now let's talk about pointers and class objects. Classes usually (if not always) hold instance variables; usually (if not always) under the ''private'' access specifier. When we have a pointer as an instance variable, a couple of problems arise. How do we allocate/deallocate dynamic memory? And how do we dereference the pointer?
The answers are pretty simple and you may have figured them out already. We allocate the memory for the instance variable in the class constructor, and deallocate that memory in the class destructor. As for dereferencing the memory, we simply use the asterisk(*) again to access the data. An example is shown below of a basic class header (.h) and implementation (.cpp).
<pre>
//file myclass.h
#ifndef MYCLASS_H
#define MYCLASS_H
class myclass
{
public:
myclass(int size);
~myclass();
void printdata() const;
private:
int *data;
int maxSize;
};
#endif
</pre>
<pre>
//file myclass.cpp
#include <iostream>
#include "myclass.h"
myclass::myclass(int size)
{
if(size < 1 || size > 10)
size = 10;
maxSize = size;
data = new int[size];
for(int i = 0; i < size; ++i)
data[i] = size - i;
}
myclass::~myclass()
{
delete [] data;
}
void myclass::printdata() const
{
for(int i = 0; i < maxSize; ++i)
cout << data[i] << endl;
}
</pre>
The above example is a simple class that holds a pointer to an array where the user is able to specify the size at run time. The memory is allocated and has a method that prints the contents of the array. One thing this example does NOT do, however, is use the asterisk(*) notation for dereferencing. This is because the only pointer is to an array, and using the data[index] notation automatically dereferences the data.
Here is the above ''void printData() const;'' using dereferencing instead of the array subscript notation ([]). It uses a process called ''pointer arithmetic'' which can be dangerous and should be avoided.
<pre>
void printdata() const
{
for(int i = 0; i < maxSize; ++i)
cout << *(data + i) << endl;
}
</pre>
What happens is the value of i is added to the memory location of data, essentially doing the same thing as an array subscript. However, it has to be dereferenced in order to not output a memory location, so the asterisk(*) is used.
Pointers to classes are possible as instance variables, they are also possible in other situations. Lets use the above class as a starting point. Here is an example of a main program that uses a pointer to a class, and calls different methods by dereferencing the class object before calling them. It uses a strange notation that combines the * with parenthesis and the member access operator (.).
<pre>
#include <iostream>
#include "myclass"
using namespace std;
int main()
{
myclass *object = new myclass(5);
//object.printdata(); //doesn't work
//*object.printdata(); //doesn't work because of operator precedence
*(object).printdata(); //or (*object).printdata();, both work
return 0;
}
//output is 5 4 3 2 1
</pre>
The above notation is messy. Dereferencing the object every time you want to access a method makes for sloppy, hard to read code. That's where the arrow operator comes in (->). It does the exact same thing as *(object).printdata(); combining the *, (), and . all in one.
<pre>
object->printdata();
//is the same as
(*object).printdata();
</pre>
When working with classes, every class has a reference to itself. This is something built into c++. The object is called ''this'', and 'this' is a reserved keyword in c++. 'this' is a pointer, and needs to be dereferenced before use.
<pre>
this->data[2];
//is the same as
data[2];
//when dealing with member functions in myclass.cpp
</pre>
When using pointers in the eoserv source, it is important to remember a couple of things. When a pointer is a parameter of a method, DO NOT delete it. Memory will be cleaned up by the emulator in the class destructor. You can have a pointer to a character and then create a new pointer that also points to that character. If you delete one, you are deleting both of them. Let's say you are making a title command:
<pre>
if (command.length() >= 5 && command.compare(0,5,"title") == 0 && arguments.size() >= 1)
{
std::string NewTitle = "";
Character *victim = this->player->character;
for (int i = 0; i < arguments.size(); ++i)
{
NewTitle += arguments[i];
NewTitle += " ";
}
if (message.length() > 32)
{
message = message.substr(0, 32);
}
victim->title = NewTitle;
//do not delete victim!
}
</pre>
|