I don't understand pointers, templates, polymorphism......

Pages: 123
closed account (z16XoG1T)
I have learned and mastered every other tutorial up to pointers(except a few before about struct that didn't appeal at all and made little sense to me).

But I just don't get pointers....I can read the page 1,000 times and it doesn't click...No clear understanding. I skip it, but templates make no sense either.

Polymorphism is the same thing; failure to understand them. And it doesn't matter how hard I try I still don't get a clear understanding of their use.

And it's causing frustration, anxiety and I'm getting tired of it.

Pointers point to things(memory addresses in hex or some thing). Okay, and what else? That's it? I don't get it. What does pointing to a memory address do for a program, and why would I need it?

Templates are sort of like classes, right? But then why is struct like a class? What's the difference? If they're all the same thing then why am I wasting time learning the same concept in three different ways that all relate to each other almost identically?

And polymorphism...What is it used for? Can any one give an example of how it would be used in, say, any thing? Any thing I might program or work with, personally?

And also encapsulation.....UHHH...I get told I need to know all of these things before considering game programming and design....But I don't see why.

I'm hopeless here.
Last edited on
closed account (z16XoG1T)
For example this code makes me sigh and facepalm...

1
2
3
4
5
6
template<class InputIterator, class T>
  InputIterator find ( InputIterator first, InputIterator last, const T& value )
  {
    for ( ;first!=last; first++) if ( *first==value ) break;
    return first;
  }


God, if only some one could make these concepts clear to me, because tutorials sure aren't helping and neither is a book.

I really want to master most of C++, but if no one helps me understand clearly....I can't.
Last edited on
pointers are a lot easier than many think, but they're often taught using clumsy analogies, which frankly are unhelpful.

A pointer type is a basic type, just like any other (e.g. int, double, char). You create one and it exists somewhere in memory.

int*, char*, double*.... they all take up the same space in memory and essentially they are just a number. Ignore for the moment that they are int pointers or char pointers or double pointers. In memory, they all look the same.

1
2
3
4
5
6
int* p_x; // You have now created a space in memory the size of a pointer.
               //  It's often the same size as an int. Right now, if you look at that memory that
               // effectively is p_x, it'll have some garbage value in it.
p_x = 0 ; // Now, if you look in that piece of memory, it'll have the number 0 in it.
p_x = 3425 ; // Now,  if you look in that piece of memory, it'll have the number 3425 in it. 


Note that in none of the above steps did you actually create a new int; you have not made an int, you have made an int*.


1
2
3
4
int y = 12; // Now we HAVE made an int somewhere in memory, with the value 12
                  // If only we knew its address in memory, we could put that address value in p_x
p_x = &y;   //  The '&' operator here returns the memory address of y, so now the value of p_x
                  //   is the address of y  




There is an operation you can carry out on a pointer called "dereferencing". This operation reaches the contents of an address in memory. The address is just a number; the number used for the address is the value of the pointer.

1
2
*p_x; // Returns (and in this case doesn't do anything with) the contents of memory address &y
          //  which in this case we know to be the number 12  




But wait, you cry. There could be anything in that memory. Anything at all. How does the compiler know how to treat it? Good question. It knows because you explained when you created p_x what kind of things it would point at. We made an int pointer, so the value returned will be treated as an int.

1
2
3
4
int z = *p_x;  // Z is now whatever value was at memory address &y, interpreted as an int
                     //   So now we've got three objects in memory (two integers, y and z, and an 
                    //    int pointer, p_x
                     //  z is 12, y is 12, and p_x is a number that is the address of y in memory  



If you try something like this:

double z = *p_z;

the compiler should warn that you're trying to assign an integer value to a double; it may carry out some kind of conversion for you, it may just refuse outright, depending on your setup. There are some special values of pointer; if your pointer has a value of 0, it is a null pointer and deliberately does not point anywhere. This is also known as a NULL pointer.

If you understand pointers like this, by actually knowing what they do in terms of the memory, they become an absolute walk in the park. Pointer arithmetic and the relationship between pointers and array follow quite easily if you recall that a pointer is just the number of some other piece of memory somewhere.


The other half of your question - why do we need pointers at all?

C and C++ trust you. If you want to mess around with raw data, the actual numbers, you can. If that raw data is hidden from you because you're using a programming language that doesn't want you to see it, that's great; it's probably safer, but don't fool yourself - some control has been wrested from your hands. C (and C++) trust the coder to a great degree; if you want to mess around at the level of specific memory addresses, you can.

Your hardware uses hard-wired address locations for various things. It is common, for example, on an embedded system for LEDs to be hard-wired to specific memory addresses. To alter the state of those LEDs, you MUST be able to specify the value in specific memory locations. If you can think of a sensible way to do that without pointers, I'd like to hear it. This holds true on the hardware in your PC. Your sound card. Your graphics card. The people who wrote those drivers couldn't do it without pointers.

When you access an array, don't fool yourself; you're using pointers. When you enter

a[10]

that's translated as

 
*(a+10)


which means "get the value of the pointer object called 'a', which is a memory address and points at a certain kind of object, and add to that value however much memory 10 of those objects would take up, and then give me the object at that memory address". Off the top of my head, I can't think of a sensible way to implement arrays without a system of just keeping track of the start location and the size of the objects.

There are more, many many more. I suspect the real problem here is that you learnt to programme using some other language that did not have these features, and the issue now is not that you can't understand these new constructs - it's that they're not part of your mental arsenal for solving problems in programming, so you never use them and thus can't understand why they exist.

When learning a new programming language, learning the new syntax is only part of it; you have to learn to think about problems using the new tools that language gives you, and that is often much, much harder. Many times, I've come across C++ code that is quite clearly just Algol or Python or Java or BASIC, but has been written in C++ (or, more commonly, written in C and called C++).

Here's an example of a real application that might make it a bit clearer. Let's say you've got an image to process, and you need to do it fast. If you read each pixel, one at a time, you've now got that many pixel objects. There could be millions of them. Millions of individual objects to now deal with. Maybe you could read in a few at a time, and deal with them, and then read the next few, but that's going to add huge amounts of time in fetching data from storage.

You could have a structure to help you keep track of them; something like image.pixel1, image.pixel2, image.pixel3..... image.pixel1000000..... lots and lots of objects. You just had to go through the construction process for each of those objects, which took time. You'll have to destruct them too; more time.

If you allocate a long stretch of memory and then just read all that pixel data directly into that memory, you've skipped a lot of construction time. You've then got a pointer to the start of the memory, so you can do this and run through the whole image a lot faster:

processPixel(*pointer_to_image++);

Additionally, you now have a mental model of the image as a long, unrolled line of pixels, that actually matches how it is in memory. You can develop your image processing algorithms based on this mental model and directly apply them. If you need to process a pixel and the output depends on the pixel to the right and left of it, that's astonishingly easy and faster than having to do some kind of structure-based fetch to get the values of those pixels.
Last edited on
closed account (z05DSL3A)
8¬|

8¬|



Indeed. I wouldn't normally blather on so much, but these days I seem to be losing my tolerance of suggestions that pointers are difficult or not useful. I must be getting old :p
I have never understood why people have so much problem with pointers either.
I think it's because pointers are commonly taught using clumsy and bad analogies, and then people have a mental model of them that is both fuzzy and, frankly, wrong.

Also, and this bit is definitely just my opinion, it's harder to understand if you don't think about them in terms of allocating space in memory, putting numbers into that space, and the operations you can carry out on those numbers - if you've got some analogy as your explanation that doesn't actually mention any of this, it's much harder. It's a real shame, as the layout of data in memory and what data is written into memory is something that we can actually understand without analogy.
Last edited on
closed account (z05DSL3A)
While I agree that pointers are not difficult when it 'clicks' but the explanation of them can be less than straight forward.

I must admit that I have not read all your post, had problems with your first example:
Moschops wrote:
1
2
3
4
5
int* p_x; // You have now created a space in memory the size of a pointer.
               //  It's often the same size as an int. Right now, if you look at that memory that
               // effectively is p_x, it'll have some garbage value in it.
p_x = 0 ; // Now, if you look in that piece of memory, it'll have the number 0 in it.
p_x = 3425 ; // Now,  if you look in that piece of memory, it'll have the number 3425 in it. 


Really? Fair enough. To me, this is the most straightforward way to explain what a pointer is; start with its existence as an object in memory and go from there.

Maybe I need a picture of some memory space :p

Alternatively, and I suspect this is the real reason, not everyone thinks in terms of objects in memory and what seems the most obvious way to me seems esoteric to others.

That first line should be expanded, really:

1
2
3
int* p_x;  // You have now created a new object, taking up space in memory.
                // This object is not an int; it is a pointer. A pointer IS a complete
                // object, taking up space in memory. 


Yes, that's not great either. I need a diagram :)

Edit: Goddammit, it's Spoonlicker again. :p
Last edited on
closed account (z05DSL3A)
Any explanation of pointers has to match the mental image of memory, that is held by the person they are explaining them to, with the explanation or it is doomed to fail.
Well maybe that's the (or just a) big problem; from conversation with some people trying to learn C, sometimes people just have no mental model of memory space.

I think that in future, when explaining pointers to someone, I'll go back one step more and start with the concept of memory space and objects in memory. Thanks, GW. I never started explaining at that point before.
I suppose (very) old folks like myself who started out years ago actually started by learing assembly programming - I learnt on the Z80 way back in 1979.
All those addressing modes - one of which was indirect memory addressing - when I stated to learn C over a decade later - and came to the pointer part - it was no hassle at all.
Last edited on
Marshall Brain's C tutorial on pointers is pretty good.
http://computer.howstuffworks.com/c21.htm
To answer other questions...

Nowadays in C++, the keyword "struct" and "class" are completely interchangeable. Before you ask, it didn't used to
be that way; originally, in C++ "struct" was identical to C structs. The only difference is default inheritance and
default access.

1
2
3
4
5
6
7
8
9
10
11
struct Derived : ThisIsAPubliclyInheritedBase /* if you don't specify the inheritance, "public" is assumed for structs
{
    /* if you don't specify the access, "public" is assumed for structs */
    int i_am_public;
};

class Derived : ThisIsAPrivatelyInheritedBase /* if you don't specify the inheritance, "private" is assumed for classes
{
    /* if you don't specify the access, "private" is assumed for classes */
    int i_am_private;
};

Templates.

Consider this silly example:

1
2
3
4
5
6
7
8
9
10
11
12
13
double add( double a, double b )
    { return a + b; }

int add( int a, int b )
    { return a + b; }

float add( float a, float b )
    { return a + b; }

unsigned add( int a, int b )
    { return a + b; }

// etc. 


So I've written the same function a bunch of times, just the parameters and return values are different.
Wouldn't it be nice if I could write this function just once and have it work for all the types? After all,
the body of the functions are absolutely identical.

Enter templates:

1
2
3
template< typename T >
T add( T a, T b )
    { return a + b; }


This says that add is a function which takes two parameters a and b of any type whatsoever, with the only restriction
at the moment being that a and b have to be the same type, and returns a value of the same type as the parameter.
This function will now accept any type whatsoever as long as instances of the type can be added together. For
example, std::string has an operator+ that concatenates two strings. This function then, given two std::strings,
returns the concatenation.

1
2
3
add( 3, 4 );   // calls add with two ints
add( 3.14, 1.414 );  // calls add with two doubles
add( std::string( "Hello" ), std::string( "World" ) );   // calls add with two std::strings 


Templates are used for generic programming. That is, I want to write an algorithm that I know works for many
times. The example you gave:

1
2
3
4
5
6
template<class InputIterator, class T>
  InputIterator find ( InputIterator first, InputIterator last, const T& value )
  {
    for ( ;first!=last; first++) if ( *first==value ) break;
    return first;
  }


This function implements a linear search. Iterators are a generic concept, the idea being that if I have a container of
elements -- perhaps an array of ints, an array of std::strings, maybe a std::vector of doubles, perhaps a std::deque
of Foos, whatever -- all I need to implement a linear search function that would work for all of these containers is
a way to represent a "reference" to the first element in the container, a way to represent a "reference" to the last
element in the container, a way to "walk" each element in sequence in the container, and a way to compare elements.

Iterators give you a way to generically reference elements within any of the above containers, along with a way to
move between them (through ++ and --). And operator== is a way to generically compare two elements for
equality.

Now there are a bunch of different kinds of iterators; the above function happens to want an input iterator. Don't
worry about that for now. Just understand that when you write

 
template< typename T, class U >


the keyword "typename" and the keyword "class" are interchangeable here, and the names you create -- in this case
T and U -- are completely arbitrary. That is, I could have written the above function this way:

1
2
3
4
5
6
template< typename Thingie, typename Watermelon >
Thingie find ( Thingie first, Thingie last, const Watermelon& value )
  {
    for ( ;first!=last; first++) if ( *first==value ) break;
    return first;
  }


and it is the same function. Just by using stupid monikers for the types, I've made it harder for someone reading
the function to know what it does.
Polymorphism.

Open up a dialog box in an application. Notice that the dialog box has a variety of different controls in it -- push buttons, check boxes, radio buttons, input bars, etc, etc.

The dialog box itself is an object, and one of its data members is a container of all the controls it has. There's a couple of ways this could be implemented. First, I could have the dialog box contain a bunch of containers -- one container for all the push buttons, one container for all the check boxes, etc etc. That kinda sucks, not only from a readability standpoint, but also, if I ever want to create a new control, I have to add another container to the dialog box class. Which may not be possible, because I don't own the dialog box class to be able to modify it and recompile it.

The second option is polymorphism. I'll derive all the controls -- push buttons, check boxes etc -- from a common base class. Then, I'll have the dialog box class contain a container of pointers to the base class. Now, if I create a new control, all I need to do is derive it from that base class, and the dialog box can automatically manage it.

That hardly does justice to the topic of polymorphism, but it's a start.
Last edited on
I just realized that this is wrong:
1
2
3
4
5
int* p_x; // You have now created a space in memory the size of a pointer.
               //  It's often the same size as an int. Right now, if you look at that memory that
               // effectively is p_x, it'll have some garbage value in it.
p_x = 0 ; // Now, if you look in that piece of memory, it'll have the number 0 in it.
p_x = 3425 ; // Now,  if you look in that piece of memory, it'll have the number 3425 in it.  


p_x = 0; makes that a null pointer and p_x = 3425; sets the pointer to point to memory address 3425. I think what you meant was *p_x = 0; and *p_x = 3425;

Anyway, to the OP, I'll take my shot at explaining.

Every variable you create in a program actually has 2 values associated with it: 1) the consumable value, 2) the address in memory. Let's take an int for example:
 
int x = 1;  // create an instance of an int and initialize its value to 1 


Now, the 'consumable' (what I mean by this is the value we would use to do computation, etc) is '1'. The address is some randomly assigned spot in memory. If you want to write a small program to demonstrate this, try:
1
2
3
4
5
6
7
8
9
10
11
#include <iostream>

int main(int argc, const char* argv[])
{
   int i = 1;

   std::cout << "Value of i = " << i << std::endl;
   std::cout << "Address in memory where value of i is contained = " << &i << std::endl;

   return 0;
}


If you run that small program several times in a row, you will notice that the address of 'i' changes pretty much every time. This shows you that an int actually has 2 values associated with it. When you write int i = 1; you are telling the computer to reserve enough space in memory (but I don't care where) to hold any possible value that int might hold and set its value to 1. So, now as demonstrated above, not only can you recall the 'value' of the variable, but you can extract the memory address of the beginning of the reserved memory space for any value that i can hold.

If you are reading that and trying the example and are still confused, maybe we should back up just a step. You need to think of memory on the computer as a huge chain of single bytes one right next to the other. [byte1][byte2][byte3] and so on. Every one of those blocks has an address associated with it. When you create a variable in your program you tell the computer: "Hey, I need you to hold on to this object for me somewhere in your memory." It's up to the OS to decide where there is a chain of blocks in a row big enough to hold whatever the data type is. To demonstrate this, re-write my first example this way:
1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
#include <string>

int main(int argc, const char* argv[])
{
   std::string i = "hello";

   std::cout << "Value of i = " << i << std::endl;
   std::cout << "Address in memory where value of i is contained = " << &i << std::endl;

   return 0;
}

As you can see, there is still an associated address for that variable, even though it is no longer of type 'int'. The address shown by this program is the address of the FIRST block of the reserved chain of blocks.

Does this help you understand anything more?
Last edited on
Ok, if you understand my previous post, let's move on.

We talked about variables and the fact that they have 2 values when created. A pointer is no different. A pointer is a variable, too. It has a 'consumable' value (a memory address) and it has its very own address in memory as well (different from the consumable). Consider this example:
1
2
3
4
5
6
7
8
9
10
11
#include <iostream>

int main(int argc, const char* argv[])
{
   int *i;

   std::cout << "Value of i = " << i << std::endl;
   std::cout << "Address in memory where value of i is contained = " << &i << std::endl;

   return 0;
}


The only thing I changed from my first example is that I declared a pointer to an int. int *i; A pointer is no different than any other variable as far as having 2 values associated with it.

Now, when we declare the pointer and don't tell it to specifically 'point' to a certain block of memory, it just points to something random. Try this:
1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>

int main(int argc, const char* argv[])
{
   int *i;

   std::cout << "Value of i = " << i << std::endl;
   std::cout << "Address in memory where value of i is contained = " << &i << std::endl;
   std::cout << "The value i points to = " << *i << std::endl;

   return 0;
}


That last 'cout' statement demonstrates 'dereferencing' a pointer. The '*' is actually an operator on the pointer variable. It says "return the value in the memory block you are pointing to". So, let's say i points to memory block 456789 (in other words, i's consumable value is 456789). Memory block 456789 contains some data in it:

-------------------------
address = 456789
value = 1
-------------------------

When we do this int x = *i; we are essentially calling a function on variable i to get its indirect value and assign that value to x. It says "Assign the value in memory address 'i' to 'x'".

Are things getting any clearer yet?
sadavied wrote:
p_x = 0; makes that a null pointer and p_x = 3425; sets the pointer to point to memory address 3425. I think what you meant was *p_x = 0; and *p_x = 3425;


I meant no such thing. The purpose of that code was to demonstrate that the pointer is a complete object in it's own right, and that it holds a value. You completely missed my point.

I meant to make it a null pointer. Then I meant to make it hold a value of 3425, which is the address is points to. Everything I said was true and correct.

If you'd read the next bit after the bit you quoted, you would have read that I stated clearly that no int had been created, just the pointer.

sadavied wrote:
Every variable you create in a program actually contains 2 values. 1) the consumable value, 2) the address in memory


That, however, is incorrect. When you create a variable in memory, it has one value. It does not inherently contain any record of its own location in memory. If you create an int, for example, you can then inspect the memory using a debugger and you will see that the only value it contains is the value of the int. It does not in any way contain its own address.
Last edited on
Sorry, wasn't trying to bust your chops. LOL

I was simply confused by the comments next to each instruction.
1
2
3
4
5
int* p_x; // You have now created a space in memory the size of a pointer.
               //  It's often the same size as an int. Right now, if you look at that memory that
               // effectively is p_x, it'll have some garbage value in it.
p_x = 0 ; // Now, if you look in that piece of memory, it'll have the number 0 in it.
p_x = 3425 ; // Now,  if you look in that piece of memory, it'll have the number 3425 in it. 


From the comments I THOUGHT you meant that you are assigning '0' to the value at that memory address and then assigning '3425' to the value at that memory address.

I apologize for the misunderstanding.
Last edited on
Pages: 123