i'm self-teaching c++ and i get how pointers work. but the doc i'm using is quite literal and the examples don't really go into why or when pointers would be used. a couple of real world examples would help me retain the knowledge.
void print_values(int* iptr, int size)
{
int i;
for (i=0; i < size; i++)
{
printf("%d ", *(iptr++));
}
printf("\n");
}
int main()
{
int table[256];
memset(table, 0, sizeof(table));
print_values(table, sizeof(table)/sizeof(int));
}
Or like a array of functions (example):
#define ___FUNC_B { func_1, func_2, func3 }
void ( __closure __fastcall * __FUNC_B [__FUNC_MAX] )( TObject* ) = ___FUNC_B;
Usage objects by pointers is in many cases is better:
CClass *ptr = new CClass();
/* something */
delete ptr;
If you have many objects and you must for example get it in a some arrange (eg. sort) you can use pointers for sort pointers to objects non objects:
vector <CClass*> Vptr;
for (i=0; i < 100; i++)
{
Vptr.push_back(new CClass());
}
sort(Vptr.begin(), Vptr.end(), __cmp_func);
/* something */
while (!Vptr.empty())
{
delete *(Vptr.begin());
Vptr.erase(Vptr.begin());
}
For dynamic memory alocation in C language:
char *memory = (char*) malloc(1024);
if (memory == NULL)
{
exit(1);
}
/* you have alocated 1KB in memory */
memory = (char*) realloc(2048);
if (memory == NULL)
{
exit(1);
}
/* you have alocated 2KB in memory */
free(memory);
/* you have nothing */
You use pointers when you want your objects to exist longer than the current stack. You can also use them to avoid copying objects into containers.
// TODO: Remember to call DeleteObjects() when you're done here!!
std::vector<MyObject*> Objects;
void Test()
{
MyObject *const pObject = new MyObject();
Objects.push_back(pObject);
}
void DeleteObjects()
{
std::vector<MyObject*>::iterator it = Objects.begin(), itEnd = Objects.end();
for (; it != itEnd; ++it)
{
delete *it;
}
Objects.clear();
}
- Uploading multiple data from a function. The caller would supply addresses of memory locations to be overwritten by the function
- Dynamic memory allocation. Allocators would return pointers to newly allocated objects.
- Passing array arguments: pass address instead of copying, to save performance for constant data.
This is not an easy question to give a short and easy answer to, and I'm sure there's plenty of resources out there talking about pointers. Basically, whenever you'd like to use indirection (which may be even recursively) you need pointers.
Say for example a binary tree data structure, where each node have pointers to it's left and right sub trees, where either might a pointing to 0
(or NULL, meaning invalid pointer) to signify there's no sub tree there. This structure might look like this (not very C++-y, but that's a different story)
struct TreeNode
{
TreeNode* left;
TreeNode* right;
}
You can't use anything BUT a pointer in this case, as it'd be an infinitely large structure.
Pointers are useful when you need a function to return more than one variable. As an example, consider you are shopping at a grocery store. Each product has a name and a price. The name would be a string and the price a double. If there were a function called "buy", and you wanted to return both the name and price of the item, you would need to use a pointer.
This is not really a C++ matter, rather it is a C matter. For the beginner level, I would love to recommend the book Understanding Pointers in C
A simple example use of pointers is in linked lists. More info on wikipedia.
whats an example usage of c++ pointers?
Pointers address the following issues:
avoiding copying large chunks of memory around. That was in C at least, in C++ the preferred method is to use references. You can still use pointers if you want though.
allocating memory at runtime. This is needed when you have no idea at compilation how much memory you will use.
remembering the addresses of (member) functions for deferred calls (and callbacks).
allocating memory that outlasts it's current scope (it's still allocated after scope is finished).
sharing an object between multiple entities (multiple objects, multiple threads and so on). This means that you can pass the address of an object around and all entities using it will access the same data, not identical copies of it.
Sometimes pointers are also used as handles. That is, if you want to allow client code to uniquely identify a chunk of data without caring (or knowing) what the data is, you cast the address of the data (the pointer) to int/some other type and pass it around as a handle. This is commonly found in APIs that offer handles to client code but don't allow client code access to the real data (see use of WinAPI's HANDLE, HWND and so on - those are pointers in the internal implementation, but you don't know - or care - what the actual data is in order to use it).
A pointer can be considered a simple variable, but instead of saving a value it saves an adress to the position of the memory that stores a value.
Think the memory as a block of drawers and in each drawer you can put a value, to make it easier to find the values, you numerus the drawers. So, a position of memory would be a drawer and the block would be the full memory.
So, when you create a pointer, for example:
int* drawer = 0;
You are referring to the drawer which is labelled with the number 0 and contains an integer value, now you may think, ok, but, how can I get that value? Well, it's simple:
int value = *drawer;
By the same way you can store a new value on that drawer (memory address):
*drawer = 15;
Now comes the fun, the block of drawers is magic, and a drawer can take you to another drawer of the block, if we label them with the same number, the value that one stores is the same in the other:
int* drawer = 0;
int* drawer_copy = drawer;
*drawer = 15;
And what happens? That "drawer_copy", which is referring to the address 0 as "drawer" allows you to access to the integer value 15.
We can also save the address of a normal variable, we use the "&" prefix to get that adress:
int value = 15;
int* drawer = &value;
If we do this now:
value = 5;
"*drawer" will return a 5.
As you can see, pointers allow the user to have more control on the memory and to have reserved memory permanently, once you have declared a pointer you can preserve an address and access to it whenever you want. :)
Okay, I've seen so many terrible responses that I feel myself obligated to add yet another one.
First things first: we are talking C++ here. So many C uses are completely invalidated.
Terrible uses of pointers
You should learn RAII: this example is completely unsafe in the face of exception
// BAD
void func(size_t n)
{
int* array = new int[n];
// .. use array
delete[] array;
}
// GOOD
void func(size_t n)
{
std::vector<int> array(n, 0);
// .. use array
}
Rule of Thumb: if you see delete
, you're doing it wrong. Chances are that if you see new
too, though it's not as true because of argument forwarding issues.
Use references whenever possible
// BAD: Contract: do not pass NULL
void func(int* i);
// GOOD
void func(int& i);
Whenever the passing NULL
does not make sense (and you don't need to rebind), use either a plain value or a (const) reference instead of a pointer.
Good uses of pointers:
Aliasing
void printSorted(std::vector<BigType> const& values)
{
std::vector<BigType*> references = from(values);
std::sort(references.begin(), references.end(), ByPointer());
std::transform(references.begin(), references.end(),
std::ostream_iterator<BigType>(std::cout, " "),
Dereference());
}
Optional Result
Object* find(Key const& key);
this is equivalent to:
boost::optional<Object&> find(Key const& key);
but quite less verbose.
Clone method
The use of a bare pointer as the return type of the clone
method is mandated by the Boost Cloneable concept:
struct Base
{
virtual ~Base() {}
virtual Base* clone() const = 0;
};
There is a sound reason: taking advantage of covariant return types for the overloading of virtual methods. This allows us to write:
struct Derived: Base
{
virtual Derived* clone() const { return new Derived(*this); }
};
Thus taking full advantage, when cloning from a Derived const&
, of the fact that we know that what is returned is at least a Derived
.
It is up to the programmer to take care of the allocated memory unfortunately, so it must be used in conjunction with Smart Containers:
std::unique_ptr<Base> u = derived.clone();