Jump to content

  • Log In with Google      Sign In   
  • Create Account

Awesome job so far everyone! Please give us your feedback on how our article efforts are going. We still need more finished articles for our May contest theme: Remake the Classics

- - - - -

C++ Workshop - Overloading & Data Type Conversion (Ch. 10)

  • You cannot reply to this topic
32 replies to this topic

#1 JWalsh   Moderators   -  Reputation: 422

Like
0Likes
Like

Posted 14 August 2006 - 10:57 AM

Welcome to the GDNet C++ Workshop – Ch. 10

For a complete introduction to this workshop, please look here. Workshop Overview This workshop is designed to aid people in their journey to learn beginning C++. This workshop is targeted at highly motivated individuals who are interested in learning C++ or who have attempted to learn C++ in the past, but found that without sufficient support and mentoring they were unable to connect all the pieces of this highly complex but powerful programming language. This is a 'guided' self-teaching C++ workshop. Each student is responsible for taking the time to read the material and learn the information. The community and tutors that arise out of this workshop are here for making the learning process run more smoothly, but are not obligated to baby-sit a person's progress. Because everyone will be working from the same textbook (Teach Yourself C++ in 21 days 5th Ed.), students may find it easier to get answers to the specific questions they might have. There is no minimum age requirement, and there is no previous programming experience required. Additionally, this workshop does not attempt to defend C++ as a language, nor does it attempt to demonstrate that C++ is either more or less useful then other programming languages for any particular purpose. People who intend to start a discussion about the differences between C++ and ANY other languages (except as are relevant to a particular discussion), are encouraged to do so elsewhere. This workshop is for educational, not philosophical discussions. Quizzes & Exercises Each week will have quizzes and exercises posted in the weekly threads. Please try and answer them by yourself. As well, please DO NOT post the answers to Quizzes and Exercises within this thread. Once it becomes acceptable to post the answers to quizzes and exercises, an additional thread will be created each week specifically for the purpose of posting quiz answers. If you try with reasonable effort but are unable to answer the questions or complete the exercises, feel free to post a clarification question here on the thread. Tutors, myself, or others will do the best we can to point you in the right direction for finding the answer.

Chapter 10 – Working with Advanced Functions

Introduction Heya all! Welcome back to the workshop...sorry I've kept you all waiting. There are many reasons for my absence, but the most relevant is that my computer melted when the CPU fan and Northbridge fan on my motherboard simultaneously stopped functioning. At any rate... This week is chapter 10. We will be focusing more on overloading member functions, operators, and constructors. We'll also take a look at initializer lists, the copy constructor, and type converters. The chapter for this week is roughly 35 pages. The primary purposes for the things learned in this chapter are convenience. By providing intelligent constructors, copy constructors, and the ability to overload operators you can make the interactions with your object more efficient and more intuitive. I will also be posting the information for Project 1 VERY soon, and it will be active for 4 weeks. People will need to draw not only from what we've learned, but from what we will learn over the next 4 weeks in order to complete the project successfully. Please remember to use OPINION and WARNING tags whenever applicable. As well, feel free to post your own insights, and review questions or exercises beginning Wednesday or Thursday. Outline of the Reading - Chapter 10
  1. Overloaded Member Functions
  2. Using Default Values
  3. Choosing Between Default Values and Overloaded Functions
  4. The Default Constructor
  5. Overloading Constructors
  6. Initializing Objects
  7. The Copy Constructor
  8. Operator Overloading
  9. Handling Data Type Conversion
  10. Conversion Operators

Good Luck!

[Edited by - jwalsh on May 30, 2007 1:01:40 PM]
Jeromy Walsh
Sr. Tools & Engine Programmer | Software Engineer
Microsoft Windows Phone Team
GameDevelopedia.com - Blog & Tutorials
GDNet Mentoring: XNA Workshop | C# Workshop | C++ Workshop
"The question is not how far, the question is do you possess the constitution, the depth of faith, to go as far as is needed?" - Il Duche, Boondock Saints

Ad:

#2 CondorMan   Members   -  Reputation: 145

Like
0Likes
Like

Posted 15 August 2006 - 03:56 AM

I've read this chapter a few times and have probably found parts of it the most confusing so far and I have a number of questions. Rather than throw them all in at once, I'll "trickle" them so I don't get mixed up any further.

I understand the concept of overloading a function - to allow a single function name to be used several times but take different numbers or types of parameters and the compiler makes sense of which actual function to use in a particular situation, determined by it's parameters. Am I correct in thinking that this is polymorphism?

I've read the section about overloading operators and I don't have a clue why this is important - but don't want to deal with that just now as there's something more fundamental that I need to clarify. In listings 10.7 and 10.8, I misinterpreted the "i" (in line 25 and 27) to be an integer but realise now that its an object of the class "Counter", which happens to be called "i". This is analagous to an object of the class "Cat" which is called "Frisky" or another one called "Boots". In each of 10.7 and 10.8, the object called "i" is incremented and it's value printed to the screen but I didn't think that an object could actually hold any data. If I move the analogy to Frisky and Boots, I've never seen anything like "cout << Frisky", but I have seen "cout << Frisky.itsAge" or "cout << Boots.itsWeight".

The basic question I have is regarding the object (Frisky, Boots or i) - can it hold data or is it the member data element (itsAge, itsWeight) which holds the data and it is this which is accessed using the dot operator (Frisky.itsAge, Boots.itsWeight etc.)?

#3 playstation   Members   -  Reputation: 146

Like
0Likes
Like

Posted 15 August 2006 - 06:47 AM

Hi all, this is nice lesson I think. Just like CondorMan I would like to know whether Object of a class are used to access member function of a class only. Member function will use data member/variables used in it or access it through parameters used in the functions?

When Constructors are used should we also use destructors? It is little confusing. I also read that when we use new operator we should also use delete? Can someone explain more in detail about constructor/destructors usage together and new/delete operator?



#4 JWalsh   Moderators   -  Reputation: 422

Like
0Likes
Like

Posted 15 August 2006 - 07:01 AM

Condor, all good questions...let me take a moment to answer them.

Quote:
I understand the concept of overloading a function - to allow a single function name to be used several times but take different numbers or types of parameters and the compiler makes sense of which actual function to use in a particular situation, determined by it's parameters. Am I correct in thinking that this is polymorphism?


No. This is not polymorphism. Polymorphism isnt when a function behaves differently based on the parameters, its when a function behaves differently based upon the object it belongs to. We'll cover it in more detail in chapter 14. Until we've covered the concept of inheritance, I cant even begin to explain it, as inheritance is fundamental to the concept of polymorphism.

Quote:
I've read the section about overloading operators and I don't have a clue why this is important - but don't want to deal with that just now as there's something more fundamental that I need to clarify.


Actually, its kind of ironic that you say this. The problem that you're having with the i++ in the rest of the this paragraph is exactly why operator overloading is important. The example in 10.8 wont make any sense unless operator overloading makes sense.

Quote:
In each of 10.7 and 10.8, the object called "i" is incremented and it's value printed to the screen but I didn't think that an object could actually hold any data.


In 10.7 the code calls i.increment(). This is just a method of the class, and as you can see on line 13, all the increment() method does is increment Counter::itsVal. On line 26 and 28 of 10.7 i.GetItsVal() is called to return the value stored in itsVal, a member variable of class Counter. So you can see now that the 'i' isnt being incremented, the value 'itsVal' is being incremented.

In 10.8 they duplicate the code for 10.7 exactly, with one small addition. On line 15 they add the operator++ method. This is operator overloading. By adding this method to the class, it tells the compiler that when someone uses the unary increment (++) operator on this class, to call that method. So on line 29 of 10.8 the Increment method is once again called. On line 31, instead of calling Increment the writer just used the '++' operator, which indirectly called the operator++ method. You'll notice that on line 14-15 both Increment and operator++ do the same thing.

In a nutshell, operator overloading just allows class writers to determine what happens to their object when used in conjunction with various operators. For example....think of a vector math class. What happens when you add two vectors together? What happens when you subtract two vectors? etc...rather than having to write:

Vector vec1(1.0, 1.0, 1.0);
Vector vec2(1.0, 1.0, 1.0);
Vector vec3 = vec1.Add( vec2 );

Operator overloading allows me to write:

Vector vec1(1.0, 1.0, 1.0);
Vector vec2(1.0, 1.0, 1.0);
Vector vec3 = vec1 + vec2;

You'll notice that now I'm able to use the '+' operator just as I would mathematically. And the '+' operator for this class knows that when you add two vectors together, you're actually adding the components of the vector together and creating a new vector. C++ doesnt know that by default.

Hope this helps. Let us know if you've got more questions or if this didnt fully answer your question.

Cheers!
Jeromy Walsh
Sr. Tools & Engine Programmer | Software Engineer
Microsoft Windows Phone Team
GameDevelopedia.com - Blog & Tutorials
GDNet Mentoring: XNA Workshop | C# Workshop | C++ Workshop
"The question is not how far, the question is do you possess the constitution, the depth of faith, to go as far as is needed?" - Il Duche, Boondock Saints

#5 RinusMaximus   Members   -  Reputation: 122

Like
0Likes
Like

Posted 15 August 2006 - 07:10 AM

I think Jeromy's example with the vectors that can be added together is a lot better than the example in the book. I understand the concept of overloading operators, but when I saw the example in the book I thought: "Now why would someone do that?".

#6 Mike.Popoloski   Members   -  Reputation: 2443

Like
0Likes
Like

Posted 15 August 2006 - 07:20 AM

Operator overloading basically makes things more intuitive and easier to read. The functions that the overloaded operators perform can be accomplished just as easily with a function, but it makes it easier for the user to understand what is going on. The vector scenario is a perfect example of how operator overloading can make things easier to read.

Here's another example. Suppose you have created a class for dealing with very large numbers, called LargeNumber. Now, it is safe to assume that the user will want to add and subtract these numbers, so you can go about this one of two ways.

1) The first way, you use functions:
LargeNumber num1 = 2;
LargeNumber num2 = 10;
num1 = num1.Add( num2 );

2) The second way, you use operators:
LargeNumber num1 = 2;
LargeNumber num2 = 10;
num1 = num1 + num2;

See how more intuitive that is?

#7 Deyja   Members   -  Reputation: 920

Like
0Likes
Like

Posted 15 August 2006 - 08:03 AM

[OPINION]
Quote:
No. This is not polymorphism. Polymorphism isnt when a function behaves differently based on the parameters, its when a function behaves differently based upon the object it belongs to. We'll cover it in more detail in chapter 14. Until we've covered the concept of inheritance, I cant even begin to explain it, as inheritance is fundamental to the concept of polymorphism.


I consider function overloading a form of polymorphism. Polymorphism means 'taking many forms', it does not mean 'inheritence with virtual functions'. Polymorphism is itself polymorphic; it takes many forms, from what most people think of - inheritence - to interface restrictions imposed by templates. Function overloading is just another form of it. From the perspective of the calling code, all those overloaded functions are the same function, and it takes different forms depending on what kind of parameters you pass to it.
[/OPINION]

[WARNING]
When overloading operators, always prefer to make them behave like they would for built-in types. To make it simple; the + operator should add, and the - operator should subtract. Don't abuse them to make them do nonsensical things. Best case, you confuse yourself and others. Worst case, wrong code compiles and does nasty things.
[/WARNING]

Hey, what are the chances of getting the OPINION and WARNING things added to the forum as actual functioning tags?

#8 TheOddMan   Members   -  Reputation: 100

Like
0Likes
Like

Posted 15 August 2006 - 08:19 AM

Quote:
Original post by Deyja
[OPINION]
Quote:
No. This is not polymorphism. Polymorphism isnt when a function behaves differently based on the parameters, its when a function behaves differently based upon the object it belongs to. We'll cover it in more detail in chapter 14. Until we've covered the concept of inheritance, I cant even begin to explain it, as inheritance is fundamental to the concept of polymorphism.


I consider function overloading a form of polymorphism. Polymorphism means 'taking many forms', it does not mean 'inheritence with virtual functions'. Polymorphism is itself polymorphic; it takes many forms, from what most people think of - inheritence - to interface restrictions imposed by templates. Function overloading is just another form of it. From the perspective of the calling code, all those overloaded functions are the same function, and it takes different forms depending on what kind of parameters you pass to it.
[/OPINION]


[MORE OPINION!]
Though I would agree that technically operator overloading is a kind of polymorphism, I think it's a little pedantic to say so. In terms of the Object-Oriented paradigm polymorphism specifically refers to functions behaving differently based on the object it belongs to, as stated by jwalsh. In this case it is the objects themselves having many forms, those forms being defined by their member functions.
[/MORE OPINION!]



#9 CondorMan   Members   -  Reputation: 145

Like
0Likes
Like

Posted 15 August 2006 - 09:10 AM

Thanks guys - it seems that my questions stimulated quite a response! I'll read the thread over a few times and then post any follow up questions for further clarification.

Just one point about polymorphism (and I speak as someone who's coming to programming languages from scratch, rather than having tried C++ previously and found it very difficult) - I've heard the term but don't know anything about it. I was under the impression that function overloading was polymorphism because of the following (page 119): "Function overloading is also called function polymorphism. Poly means many, and morph means form: A polymorphic function is many-formed".

I realise that there is discrepancy in that some of you do regard this as polymorphism whilst others don't.


#10 JWalsh   Moderators   -  Reputation: 422

Like
0Likes
Like

Posted 15 August 2006 - 10:33 AM

Let me clarify....

The term polymorphism (lowercase p) refers to the ability of something to take on multiple forms. Function polymorphism then is the ability of a function to 'appear' to behave differently depending on the circumstances. In reality, there exists different functions that perform different actions, but the compiler selects the correct function to call depending upon the parameter list at compile-time. So in this regard, the function is polymorphic.

Polymorphism (capital p) is an Object Oriented term which describes an object's ability to behave as though it were a class different than that which is indicated. This is possible due to something called the Virtual Function Table which we will explore in more depth in the next 2 chapters (inheritance and polymorphism).

A quick example for the overzealous.

class BaseClass
{
public:

void MyControlFunction()
{
cout << "This is just a control function..." << endl;
}

virtual void MyFunction( void )
{
cout << "I belong to BaseClass" << endl;
}
};

class DerivedClass : public BaseClass
{
public:

virtual void MyFunction( void )
{
cout << "I belong to DerivedClass" << endl;
}
}

//.....
// some time later
//.....

// Create a base class object
BaseClass* pBaseClass = new BaseClass;

// Create a derived class object
DerivedClass* pDerivedClass = new DerivedClass;

// Create a derived class object, but store it in a pointer to a base class
BaseClass* pPolymorphicClass = new DerivedClass;

//----------------------------------------
// now lets call the output function on the pointer to the base class to
// see what happens
//----------------------------------------

// This outputs "This is just a control function...", which makes sense
// as this is a pointer to Base Class and MyControlFunction is a method of
// BaseClass
pBaseClass->MyControlFunction();

// This outputs "I belong to BaseClass" as you might expect
pBaseClass->MyFunction();

//----------------------------------------
// now lets call the output function on the pointer to the derived class to
// see what happens
//----------------------------------------

// This outputs "This is just a control function..." The reason being
// is that pDerivedClass inherited the functionality from BaseClass.
// So even though its not explicitly declared in DerivedClass, it's still
// available to be called
pDerivedClass->MyControlFunction();

// This outputs "I belong to DerivedClass"...This is because DerivedClass
// overrides the function, thus "hiding" the implementation of BaseClass from
// the outside world
pDerivedClass->MyFunction();

//----------------------------------------
// now lets call the output function on the base pointer to the derived class to
// see what happens
//----------------------------------------

// This outputs "This is just a control function..." The reason being
// is that pPolymorphicClass is a pointer to a base class, and the function
// is not virtual, so it calls the function which belongs to the type of the
// pointer, in this case BaseClass*
pPolymorphicClass->MyControlFunction();

// This outputs "I belong to DerivedClass"...This is the tricky part. Even
// though this is a BaseClass pointer, the pointer actually points to a Derived
// Class. Since the method 'MyFunction' is virtual, the compiler instead
// determines which method to call based on the object being pointed to, not
// the type of the pointer.
pPolymorphicClass->MyFunction();



So now hopefully you can see that by using inheritance, and a combination of base pointers to a derived class you can create unique solutions in which objects sometimes act like the base class (or any ancestor) and sometimes acts like the type being pointed to. This is "Polymorphism" (Capital P) in the Object Oriented paradigm.

Cheers!

Jeromy Walsh
Sr. Tools & Engine Programmer | Software Engineer
Microsoft Windows Phone Team
GameDevelopedia.com - Blog & Tutorials
GDNet Mentoring: XNA Workshop | C# Workshop | C++ Workshop
"The question is not how far, the question is do you possess the constitution, the depth of faith, to go as far as is needed?" - Il Duche, Boondock Saints

#11 Deyja   Members   -  Reputation: 920

Like
0Likes
Like

Posted 15 August 2006 - 11:28 AM

Perhaps, when we get to that chapter, we should spend some time explaining the difference between compile-time polymorphism and run-time polymorphism.

#12 CondorMan   Members   -  Reputation: 145

Like
0Likes
Like

Posted 15 August 2006 - 08:11 PM

Thank you for the further clarification.

It's another example of the book's ambiguity and how this group is absolutely invaluable in teaching C++ because it allows such matters to be thrashed out. Another example of ambiguity that I recall is when the convention for naming pointers with an initial lower case "p" wasn't followed. It took me some time for that to click in a piece of code for me whereas I suspect that the experienced of you realised immediately what was happening!

Back to this thread - having slept on the explanations about i.increment() and ++i, I had realised that the former was a method of the class. What I hadn't appreciated was that the '++' operator called the operator++ indirectly. I thought that ++i was the normal "increment then fetch" acting on an integer. I think that if the examples in the book had been to create an object of the class Counter called "number" (for instance), rather than "i", I wouldn't have jumped into the assumption that "i" was an integer, rather than an object.

#13 RinusMaximus   Members   -  Reputation: 122

Like
0Likes
Like

Posted 15 August 2006 - 10:40 PM

I have a question that is not chapter related, or at least not to the chapters we've read so far. Are there any guidelines on how to make comments in C++ code?

In java there is something called javadoc in which you can document methods like:

/**
* This method is commented with javadoc.
* @param parm A parameter to show how to document a parameter in java.
* @return An int showing how to document a return type in java.
*/
public int commentedMethod(int parm){
}


Does C++ have a similar system for making comments?

#14 Deyja   Members   -  Reputation: 920

Like
0Likes
Like

Posted 15 August 2006 - 11:42 PM

No. However, Doxygen (google it) can use java-doc style comments.

#15 Captain P   Members   -  Reputation: 1084

Like
0Likes
Like

Posted 16 August 2006 - 01:08 AM

Besides Doxygen, I find using (commented) header files as a sort of reference chart usefull as well. That´s personal preference however, do what you´re most comfortable with - or when at work, what the company guidelines say. ;)

As for operator overloading, think about assignment operators, like =, -= and so on. The basic type assignment operators return a reference to themselves, which makes statements like 'a = b = c = 2;' possible. Consider doing the same for your assignment operators. Also, watch out for self-assignment. I don't know if the book describes that, but hey. :)
Create-ivity - a game development blog Mouseover for more information.

#16 Zahlman   Moderators   -  Reputation: 1666

Like
0Likes
Like

Posted 16 August 2006 - 06:03 AM

Edit: This is my very late take on it. You might also want to look at some of these pages.

Quote:
Original post by CondorMan
I understand the concept of overloading a function - to allow a single function name to be used several times but take different numbers or types of parameters and the compiler makes sense of which actual function to use in a particular situation, determined by it's parameters. Am I correct in thinking that this is polymorphism?


This is one form of polymorphism. There are many others.

Usually when people use the term, they are thinking of dynamic polymorphism brought-about by subtyping, which in C++ is normally done by inheritance (i.e. class Derived : public Base { /* */ }; Base* thing = new Derived(); thing->doVirtualFunction(); // calls Derived::doVirtualFunction).

Quote:
The basic question I have is regarding the object (Frisky, Boots or i) - can it hold data or is it the member data element (itsAge, itsWeight) which holds the data and it is this which is accessed using the dot operator (Frisky.itsAge, Boots.itsWeight etc.)?


That sort of depends on what you think "hold data" means.

A member is *part of* the object. In memory somewhere, there is a sequence of bytes that represents a Cat (or a Counter). Some of those bytes within a Cat represent itsAge (or itsWeight). Some of those bytes within a Counter represent the counter value.

Whenever a data member is public, the outside code can access just that data member directly. When the data member is of a type that supports printing (via operator<<(ostream&, const whatever_type&) ), it could be printed that way. Out of box, all the primitive types (like 'int') support that. However, classes are types, so they can be made to support such printing - but you have to implement it (via the above-mentioned operator), so that the compiler knows how you want it done.

For example, you could support it for a Cat:


ostream& operator<<(ostream& os, const Cat& c) {
return os << "A Cat that is " << c.itsAge << " years old and weighs " << c.itsWeight << "pounds";
}


And now you can 'cout << Frisky << " and " << Boots;'. :)

#17 Deyja   Members   -  Reputation: 920

Like
0Likes
Like

Posted 16 August 2006 - 08:52 AM

Quote:
Also, watch out for self-assignment.


OPINION : When I see an assignment operator that has to check for self-assignment to function properly, I smell design flaws. Specifically, an assignment operator that is exception-safe is probably self-assignment safe too. Unfortunatly, you haven't got the tools yet to do it 'properly', and it's not an idiom that made any sense to me when I was first starting out. You can look up RAII and the swap-with-temp idiom if you want to get into it.
A check for self assignment can still be used for optimization, but only when a profiler tells you it counts. For example, it would be stupid for a mathmatical vector class to check for self assignment. That check is extra code that has to run for every single vector assignment, to stop a few extra copies of built in types in a very small percentage of cases.
Speaking of mathmatical vectors; like any mathmatical quantity, I would expect that, after X -= X; that X == 0. If your 'check for self assignment' breaks that condition, I wouldn't consider your vector to be correct.

#18 Zahlman   Moderators   -  Reputation: 1666

Like
0Likes
Like

Posted 16 August 2006 - 12:46 PM

Quote:
Original post by Deyja
Quote:
Also, watch out for self-assignment.


OPINION : When I see an assignment operator that has to check for self-assignment to function properly, I smell design flaws. Specifically, an assignment operator that is exception-safe is probably self-assignment safe too. Unfortunatly, you haven't got the tools yet to do it 'properly', and it's not an idiom that made any sense to me when I was first starting out. You can look up RAII and the swap-with-temp idiom if you want to get into it.


It's more often referred to, AFAIK, as the copy-and-swap idiom, in case you're trying to Google it.

(BTW, I agree with Deyja's opinion.)



#19 Deyja   Members   -  Reputation: 920

Like
0Likes
Like

Posted 16 August 2006 - 03:52 PM

Actually I wasn't sure it had a name. I call it the swap-with-temp (or swap-with-copy) idiom because that's how the code reads:


some_type& some_type::operator=(const some_type& rhs)
{
this->swap( some_type(rhs) ); //copy implemented in terms of copy constructor
return *this;
}


It literally reads 'swap with a temporary'. :)

#20 dalleboy   Members   -  Reputation: 324

Like
0Likes
Like

Posted 23 August 2006 - 09:04 AM

Quote:
Original post by Deyja
Actually I wasn't sure it had a name. I call it the swap-with-temp (or swap-with-copy) idiom because that's how the code reads:


some_type& some_type::operator=(const some_type& rhs)
{
this->swap( some_type(rhs) ); //copy implemented in terms of copy constructor
return *this;
}


Wouldn't this be better/more standard, as it doesn't convert the temporary to a non-const reference.

some_type& some_type::operator=(const some_type& rhs)
{
some_type(rhs).swap(*this); //copy implemented in terms of copy constructor
return *this;
}



Arguing on the internet is like running in the Special Olympics: Even if you win, you're still retarded.[How To Ask Questions|STL Programmer's Guide|Bjarne FAQ|C++ FAQ Lite|C++ Reference|MSDN]





PARTNERS