Sign in to follow this  
Assassin7257

Explain this set of Code

Recommended Posts

[code]
#include<iostream>
#include<string>
using namespace std;

class car
{
public:
car(const string& name = " "); //cars name
string getName() const; //returns name
car* getNext() const;
void setNext(car* next);

private:
string m_name; //naame of car
car* m_pNext; //pointer to next car on the list

};

car::car(const string& name):
m_name(name),
m_pNext(0)
{}

string car::getName() const
{
return m_name;
}

car* car::getNext() const
{
return m_pNext;
}

void car::setNext(car* next)
{
m_pNext = next;
}

class Garage
{
friend ostream& operator<<(ostream& os, const Garage& aGarage);

public:
Garage();
~Garage();
void AddCar();
void DeleteCar();
void Clear();

private:
car* m_pHead;
};

Garage::Garage():
m_pHead(0)
{}

Garage::~Garage()
{
Clear();
}

void Garage::AddCar()
{
//getting new car
cout << "Please enter new name of car: " << endl;
string name;
cin >> name;

car* pNewCar = new car(name);

//if list is empty make new new head
if( m_pHead == 0)
{
m_pHead = pNewCar;
}

//otherwise end of the list
else
{
car* pIter = m_pHead;
while(pIter->getNext() != 0)
{
pIter = pIter->getNext();
}
pIter->setNext(pNewCar);
}
}

void Garage::DeleteCar()
{
if(m_pHead == 0)
{
cout << "The garage is empty !" << endl;
}
else
{
car* pTemp = m_pHead;
m_pHead = m_pHead->getNext();
delete pTemp;
}
}

void Garage::Clear()
{
while(m_pHead != 0)
{
DeleteCar();
}
}

ostream& operator<<(ostream& os, const Garage& aGarage)
{
car* pIter = aGarage.m_pHead;

os << "\nHere's what cars are in the garage:\n";
if(pIter == 0)
{
os << "The garage is empty.\n";
}

else
{
while(pIter != 0)
{
os << pIter->getName() << endl;
pIter = pIter->getNext();
}
}

return os;
}

int main()
{
Garage myGarage;
int choice;

do
{
cout << myGarage;
cout << "Here you'll have your own car garage.\n";
cout << "0 - Quit" << endl;
cout << "1 - Add Car" << endl;
cout << "2 - Delete Car" << endl;
cout << "3 - Clear Car" << endl;
cin >> choice;

switch(choice)
{
case 0:cout << "Thanks for Playing" << endl; break;
case 1:myGarage.AddCar(); break;
case 2:myGarage.DeleteCar(); break;
case 3:myGarage.Clear(); break;
default: cout << "Invalid choice.." << endl;
}
}while(choice != 0);

return 0;
}
[/code]

This is a some code I got from reading the Beginning C++ through Game Programming, it explains it in the book, but I really don't understand. I was wondering if anyone could help explain. I get really confused around the Garage Add member fucntion, that's what confuses most.

Share this post


Link to post
Share on other sites
What specifically confuses you about it? The general idea is this: get the name of the car and create a new car, and then add it to a linked list. My guess is it's the adding to the linked list part that's confusing you, but I'm not sure which part of it is confusing, so I'm not sure what more to say at this point. If you google how to make a linked list in C++, you should see other examples that may answer your question. Or you can give greater clarity on what you do/don't understand and we can try and explain a little more.

Share this post


Link to post
Share on other sites
I don't understand how the add function really works, the part that really confuses me is the else statement. So this how I think it goes. pIter points to the m_pHead, I get lost at the while statement. Does is keep looping until pIter equals a null pointer ? Then after that pIter sets next, so then pNext is equal to pIter.

Share this post


Link to post
Share on other sites
[quote name='Assassin7257' timestamp='1330227557' post='4916648']
I don't understand how the add function really works, the part that really confuses me is the else statement. So this how I think it goes. pIter points to the m_pHead, I get lost at the while statement. Does is keep looping until pIter equals a null pointer ? Then after that pIter sets next, so then pNext is equal to pIter.
[/quote]
Yes, exactly, except for that last part. It sets the last next value to the new car it just created. Think of it as a long chain. Each car has a pointer to the next car, chaining them together. The while loop just follows the head to the end of the chain (or the linked list, as we call it in computer science), and once it reaches the end of the chain (i.e. a null pointer, because the last car isn't linked to anything after it), it appends the car to the end of this chain (by setting pIter's next to the new car that was created).

Share this post


Link to post
Share on other sites
[quote name='Assassin7257' timestamp='1330229035' post='4916655']
so pIter works almost like an iter, using a while loop. and m_pHead is the head of the chain.
[/quote]
Yes, which is why it was named pIter (i.e. "pointer iterator," I'm guessing is what they meant when they named it that). They use it to iterate through the linked list until the end, and then append the new data once they find the end.

Share this post


Link to post
Share on other sites
So every object thats created using the add member function. Has a m_pNext pointer beside it. So when the add function is called, the pointer pIter point to m_pHead which is in front of the chain, the while loop continues until pIter is a null pointer. In the loops body, it returns the address of each m_pNext for each object. Once its gets to zero, pIter calls setNext which equals zero and initlizes pNewCar to it.

Share this post


Link to post
Share on other sites
[code]
void Garage::AddCar()
{
//getting new car
cout << "Please enter new name of car: " << endl;
string name;
cin >> name;

car* pNewCar = new car(name); // Create an instance of car, initialing it with name.

if( m_pHead == 0) // If there are no cars in our list (i.e. m_pHead is 0)
{
m_pHead = pNewCar; // Set the car we just created to be the head of the list.
}
else // If there are cars in the list
{
car* pIter = m_pHead; // Get a pointer to the first car in the list
while(pIter->getNext() != 0) // While their IS another car in the list (i.e. getNext() returns non-null)
{
pIter = pIter->getNext(); // Move to the next car in the list.
}
pIter->setNext(pNewCar); // pIter now points to the last car in the list, insert our new car after it.
}
}
[/code]

Share this post


Link to post
Share on other sites
So pIter starts off at the first address of the list in this case m_pHead, keeps moving done to the list, and once it reaches the last object it goes straight back to 0 or the next memory address, and assigns the new car to it.

Share this post


Link to post
Share on other sites
No. pIter never goes to null. It goes to the very last element in the list, whose NEXT pointer is null.

Note: Null. Not zero. The use of 0 here is confusing, but we are talking about NULL pointers, not "zero" pointers.

Share this post


Link to post
Share on other sites
[quote name='Washu' timestamp='1330233013' post='4916664']
Note: Null. Not zero. The use of 0 here is confusing, but we are talking about NULL pointers, not "zero" pointers.
[/quote]

Sorry Washu, I might be wrong here but is there any difference between NULL pointers and "zero" pointers ? I thought they are the same. [img]http://public.gamedev.net//public/style_emoticons/default/wacko.png[/img]

In Dr. Stroustrup's own words -
[i]"No object is allocated with the address 0. Consequently, 0 acts as a pointer literal, indicating that a pointer doesn’t refer to an object.
In C, it has been popular to define a macro NULL to represent the zero pointer. Because of C++’s tighter type checking, the use of plain 0, rather than any suggested NULL macro, leads to fewer problems."[/i]

Share this post


Link to post
Share on other sites
[quote name='Washu' timestamp='1330233013' post='4916664']
No. pIter never goes to null. It goes to the very last element in the list, whose NEXT pointer is null.

Note: Null. Not zero. The use of 0 here is confusing, but we are talking about NULL pointers, not "zero" pointers.
[/quote]

All a pointer stores is a number that is interpreted as a memory location, so setting a pointer to 0 makes it point to location 0 in the memory. Now the NULL define is defined as following
[code]#define NULL 0[/code]
So writing 0 instead of NULL is exactly the same thing as the preprocessor only does a textual replace in the source code. 0 is often even more correct because libraries tend to re-define this variable for their own use, if I type in NULL in my current code base and do a symbol search with VAX the list it gives me of possible defines is longer than what fits horizontally on a 24" monitor.

Share this post


Link to post
Share on other sites
And thanks to the new standard the discussion is (finally) obsolete. Get a recent enough compiler and use nullptr. No more ambiguity, no more macros.

Share this post


Link to post
Share on other sites
[quote name='NightCreature83' timestamp='1330446490' post='4917417']
[quote name='Washu' timestamp='1330233013' post='4916664']
No. pIter never goes to null. It goes to the very last element in the list, whose NEXT pointer is null.

Note: Null. Not zero. The use of 0 here is confusing, but we are talking about NULL pointers, not "zero" pointers.
[/quote]

All a pointer stores is a number that is interpreted as a memory location, so setting a pointer to 0 makes it point to location 0 in the memory. Now the NULL define is defined as following
[code]#define NULL 0[/code]
So writing 0 instead of NULL is exactly the same thing as the preprocessor only does a textual replace in the source code. 0 is often even more correct because libraries tend to re-define this variable for their own use, if I type in NULL in my current code base and do a symbol search with VAX the list it gives me of possible defines is longer than what fits horizontally on a 24" monitor.
[/quote]
[quote name='Marvel Magnum' timestamp='1330431915' post='4917371']
[quote name='Washu' timestamp='1330233013' post='4916664']
Note: Null. Not zero. The use of 0 here is confusing, but we are talking about NULL pointers, not "zero" pointers.
[/quote]

Sorry Washu, I might be wrong here but is there any difference between NULL pointers and "zero" pointers ? I thought they are the same. [img]http://public.gamedev.net//public/style_emoticons/default/wacko.png[/img]

In Dr. Stroustrup's own words -
[i]"No object is allocated with the address 0. Consequently, 0 acts as a pointer literal, indicating that a pointer doesn’t refer to an object.
In C, it has been popular to define a macro NULL to represent the zero pointer. Because of C++’s tighter type checking, the use of plain 0, rather than any suggested NULL macro, leads to fewer problems."[/i]
[/quote]
When you assign the literal 0 to a pointer you're not assigning the integer value 0 to the pointer. Don't ever confuse a memory address with "just another number" because memory addresses ARE NOT just another integer. The literal value of 0 when assigned to a pointer takes on the meaning of reassigning a pointer to be null. I.e. not pointing to anything. That DOES NOT MEAN that the VALUE of the pointer is zero. In fact, you can see this in practice quite frequently. Many debuggers will change the values of pointers to special values for error reporting purposes.

On some systems I've used assigning 0 to a null pointer produced pointers that weren't 0, as 0 was a very legitimate address where you would frequently find yourself offsetting from, but was in fact the opposite end, ~((size_t)0). It's important to understand that a NULL POINTER is not a pointer that points to the literal address "0". It is simply "A NULL POINTER." Sounds confusing? Welcome to C++. You might be wondering, at this point, "if a null pointer's value might not be null, then how does this test work 'if(x == 0)', the answer is simple... you're comparing against literal zero, which ends up being implicitly promoted to a pointer type of x and is hence a null pointer.

This is why I said that it's "confusing." Saying "zero-pointer" is incorrect because it ASSUMES that the value of the pointer is 0, whereas it is a null pointer.
[quote name='Trienco' timestamp='1330456941' post='4917478']
And thanks to the new standard the discussion is (finally) obsolete. Get a recent enough compiler and use nullptr. No more ambiguity, no more macros.
[/quote]

Yes, thankfully nullptr is in the new standard, and even supported in older compilers like 2008. You can even provide an implementation available for almost any compiler.

Share this post


Link to post
Share on other sites
[quote name='Washu' timestamp='1330466659' post='4917522']
It's important to understand that a NULL POINTER is not a pointer that points to the literal address "0". It is simply "A NULL POINTER."
This is why I said that it's "confusing." Saying "zero-pointer" is incorrect because it ASSUMES that the value of the pointer is 0, whereas it is a null pointer.
[/quote]
Sorted! :)

Share this post


Link to post
Share on other sites
[quote name='Washu' timestamp='1330466659' post='4917522']
On some systems I've used assigning 0 to a null pointer produced pointers that weren't 0, as 0 was a very legitimate address where you would frequently find yourself offsetting from, but was in fact the opposite end, ~((size_t)0). It's important to understand that a NULL POINTER is not a pointer that points to the literal address "0". It is simply "A NULL POINTER." Sounds confusing? Welcome to C++. You might be wondering, at this point, "if a null pointer's value might not be null, then how does this test work 'if(x == 0)', the answer is simple... you're comparing against literal zero, which ends up being implicitly promoted to a pointer type of x and is hence a null pointer.
[/quote]
Just to show you that VSC++ actually does a check against a 0 literal and not your null pointer construct, code compiled under VS2010 C++ Express edition in debug and shown in disassembly view.
[code]
int* test = 0;
010413DE mov dword ptr [test],0
int test2;
test2 = 32;
010413E5 mov dword ptr [test2],20h
test = &test2;
010413EC lea eax,[test2]
010413EF mov dword ptr [test],eax

if (test == 0)
010413F2 cmp dword ptr [test],0
010413F6 jne wmain+41h (1041401h)
{
*test = 40;
010413F8 mov eax,dword ptr [test]
010413FB mov dword ptr [eax],28h
}

return 0;
01041401 xor eax,eax
[/code]

Now can you explain to me why the cmp is just using a 0 literal and not your null pointer construct? And after initialisation of test the actual value of the pointer is 0 in this code.

For completeness here is the code with using NULL instead of 0
[code]
int* test = NULL;
013D13DE mov dword ptr [test],0
int test2;
test2 = 32;
013D13E5 mov dword ptr [test2],20h
test = &test2;
013D13EC lea eax,[test2]
013D13EF mov dword ptr [test],eax

if (test == NULL)
013D13F2 dword ptr [test],0
013D13F6 jne wmain+41h (13D1401h)
{
*test = 40;
013D13F8 mov eax,dword ptr [test]
013D13FB mov dword ptr [eax],28h
}

return 0;
013D1401 xor eax,eax
[/code]

As a side note the only instructions used here are a move(mov), jump not equal(jne), xor (exlusive or) and load effective address(lea), and all LEA does is store the address of the right parameter in the left one.

Share this post


Link to post
Share on other sites
Hidden
[quote name='NightCreature83' timestamp='1330474390' post='4917562']
[quote name='Washu' timestamp='1330466659' post='4917522']
On some systems I've used assigning 0 to a null pointer produced pointers that weren't 0, as 0 was a very legitimate address where you would frequently find yourself offsetting from, but was in fact the opposite end, ~((size_t)0). It's important to understand that a NULL POINTER is not a pointer that points to the literal address "0". It is simply "A NULL POINTER." Sounds confusing? Welcome to C++. You might be wondering, at this point, "if a null pointer's value might not be null, then how does this test work 'if(x == 0)', the answer is simple... you're comparing against literal zero, which ends up being implicitly promoted to a pointer type of x and is hence a null pointer.
[/quote]
Just to show you that VSC++ actually does a check against a 0 literal and not your null pointer construct, code compiled under VS2010 C++ Express edition in debug and shown in disassembly view.
[code]
int* test = 0;
010413DE mov dword ptr [test],0
int test2;
test2 = 32;
010413E5 mov dword ptr [test2],20h
test = &test2;
010413EC lea eax,[test2]
010413EF mov dword ptr [test],eax

if (test == 0)
010413F2 cmp dword ptr [test],0
010413F6 jne wmain+41h (1041401h)
{
*test = 40;
010413F8 mov eax,dword ptr [test]
010413FB mov dword ptr [eax],28h
}

return 0;
01041401 xor eax,eax
[/code]

Now can you explain to me why the cmp is just using a 0 literal and not your null pointer construct? In this code the value of test pointer after initialisation is actually 0[/quote]

Share this post


Link to post
[quote name='NightCreature83' timestamp='1330474390' post='4917562']
Now can you explain to me why the cmp is just using a 0 literal and not your null pointer construct?
[/quote]
Because the null pointer concept exists in C++, and you're looking at the machine code. On machine code level, the null pointer has to have a physical representation somehow, and your compiler just happened to choose the value 0 for the null pointer. But as I said, you're looking at the null pointer concept in an entirely different language that in which the concept exists.

There is absolutely no guarantee or reason why that '0' in your C++ code has to be translated into a '0' on the machine code level. He even gave you an example of a common null pointer (all one-bits; ~0) in cases where the physical memory address 0 is a valid address, so the null pointer concept cannot be mapped to the address 0 in such cases.

Share this post


Link to post
Share on other sites
Just to expand on what Washu and Brother Bob have said, the C++03 and C++11 standards state:

[quote]
[b][url="http://cs.nyu.edu/courses/summer11/G22.2110-001/documents/c++2003std.pdf"]http://cs.nyu.edu/co.../c++2003std.pdf[/url] [/b]and[b] [url="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf"]http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf[/url][/b]

The macro NULL, defined in any of <clocale>, <cstddef>, <cstdio>, <cstdlib>, <cstring>,
<ctime>, or <cwchar>, is an implementation-defined C + + null pointer constant in this International
Standard (18.1)
[/quote]

So there is no guarantee that it will be zero.

[quote name='Washu' timestamp='1330466659' post='4917522']
*snip*
[/quote]
[quote name='Brother Bob' timestamp='1330474914' post='4917566']
*snip*
[/quote]
Question about C++11 though: isn't comparing/assigning a pointer to zero the same as comparing/assigning it to nullptr? Not that this mandates that the actual machine code uses 0 as the value for nullptr, but am I correct in understanding that the compiler will map 0 to nullptr when dealing with pointer types?

Share this post


Link to post
Share on other sites
Any integer constant expression rvalue (C++03)/prvalue(C++11) that evaluates to zero will be considered a null pointer constant when mapped to a pointer type. This includes 0, (1 & 2), (3 - 2 - 1) or any of the other combinations of symbols that create an integer constant expression that evaluates to zero.

Share this post


Link to post
Share on other sites
I believe the standard allows for the NULL to expand to something other than the integer literal 0, for example it would be allowed to expand to [i]nullptr[/i] in C++11 compilers. As an aside, the [url="http://herbsutter.com/2012/02/08/going-native-sessions-online/"]Going Native talk[/url] by Stephan T. Lavavej mentioned that he tried this and it broke [b]a lot[/b] of code, but they found a lot of bugs too. So I don't imagine this will be the case.

[quote]
Any integer constant expression rvalue (C++03)/prvalue(C++11) that evaluates to zero will be considered a null pointer constant when mapped to a pointer type. This includes 0, (1 & 2), (3 - 2 - 1) or any of the other combinations of symbols that create an integer constant expression that evaluates to zero.
[/quote]
I've always wondered whether there was a practical application for the flexibility of this rule, beyond the literal 0 value...

Share this post


Link to post
Share on other sites
[quote name='Trienco' timestamp='1330456941' post='4917478']
And thanks to the new standard the discussion is (finally) obsolete. Get a recent enough compiler and use nullptr. No more ambiguity, no more macros.
[/quote]

true.. and the stl made also the code quoted from the book totally archaic and error prone long time ago.
All is needed is a:

vector< Car*> cars;

member in Garage.. and the addCar function would be as simple as cars.push_back( new Car() );

In C++11 a bit uglier but with the plus of automatic deletion:

vector< shared_ptr<Car> > cars;

cars.push_back( make_shared<Car>() );

Share this post


Link to post
Share on other sites
Well, if dispensing with the intrusive list we can probably drop the heap allocations altogether:
[code]
class Garage
{
public:
// ...
void addCar()
{
cars.push_back(Car());
// alternatively, in C++11, this creates a car inside the vector (rather than copying one in)
cars.emplace_back();
}

private:
std::vector<Car> cars;
}
[/code]

Share this post


Link to post
Share on other sites
[quote name='rip-off' timestamp='1330514652' post='4917695']
I've always wondered whether there was a practical application for the flexibility of this rule, beyond the literal 0 value...
[/quote]
This is the explanation I've heard: programmers frequently misuse the NULL macro, such as assigning it to integers. Sometimes some issues can be detected by using a null pointer constant other than 0. For example, if pointers are 64-bit and ints are 32-bit, assigning 0 to an int won't give you warning that an int doesn't have enough bits to represent an actual full pointer. On the other hand, if long long could hold that value you can #define NULL ((long long)0) which would still be a legal null pointer constant but would also tell you that your destination size is too small if you tried assigning to a regular int.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this