Search

9.x — Chapter 9 comprehensive quiz

In this chapter, we explored topics related to operator overloading, as well as overloaded typecasts, and topics related to the copy constructor.

Summary

Operator overloading is a variant of function overloading that lets you overload operators for your classes. When operators are overloaded, the intent of the operators should be kept as close to the original intent of the operators as possible. If the meaning of an operator when applied to a custom class is not clear and intuitive, use a named function instead.

Operators can be overloaded as a normal function, a friend function, or a member function. The following rules of thumb can help you determine which form is best for a given situation:

  • If you’re overloading assignment (=), subscript ([]), function call (()), or member selection (->), do so as a member function.
  • If you’re overloading a unary operator, do so as a member function.
  • If you’re overloading a binary operator that modifies its left operand (e.g. operator+=), do so as a member function if you can.
  • If you’re overloading a binary operator that does not modify its left operand (e.g. operator+), do so as a normal function or friend function.

Typecasts can be overloaded to provide conversion functions, which can be used to explicitly or implicitly convert your class into another type.

A copy constructor is a special type of constructor used to initialize an object from another object of the same type. Copy constructors are used for direct/uniform initialization from an object of the same type, copy initialization (Fraction f = Fraction(5,3)), and when passing or returning a parameter by value.

If you do not supply a copy constructor, the compiler will create one for you. Compiler-provided copy constructors will use memberwise initialization, meaning each member of the copy is initialized from the original member. The copy constructor may be elided for optimization purposes, even if it has side-effects, so do not rely on your copy constructor actually executing.

Constructors are considered converting constructors by default, meaning that the compiler will use them to implicitly convert objects of other types into objects of your class. You can avoid this by using the explicit keyword in front of your constructor. You can also delete functions within your class, including the copy constructor and overloaded assignment operator if desired. This will cause a compiler error if a deleted function would be called.

The assignment operator can be overloaded to allow assignment to your class. If you do not provide an overloaded assignment operator, the compiler will create one for you. Overloaded assignment operators should always include a self-assignment check.

New programmers often mix up when the assignment operator vs copy constructor are used, but it’s fairly straightforward:

  • If a new object has to be created before the copying can occur, the copy constructor is used (note: this includes passing or returning objects by value).
  • If a new object does not have to be created before the copying can occur, the assignment operator is used.

By default, the copy constructor and assignment operators provided by the compiler do a memberwise initialization or assignment, which is a shallow copy. If your class dynamically allocates memory, this will likely lead to problems, as multiple objects will end up pointing to the same allocated memory. In this case, you’ll need to explicitly define these in order to do a deep copy. Even better, avoid doing your own memory management if you can and use classes from the standard library.

Quiz Time

1) Assuming Point is a class and point is an instance of that class, should you use a normal/friend or member function overload for the following operators?

1a) point + point
1b) -point
1c) std::cout << point
1d) point = 5;

Show Solution

2) Write a class named Average that will keep track of the average of all integers passed to it. Use two members: The first one should be type int32_t, and used to keep track of the sum of all the numbers you’ve seen so far. The second should be of type int8_t, and used to keep track of how many numbers you’ve seen so far. You can divide them to find your average.

2a) Write all of the functions necessary for the following program to run:

int main()
{
	Average avg;
	
	avg += 4;
	std::cout << avg << '\n'; // 4 / 1 = 4
	
	avg += 8;
	std::cout << avg << '\n'; // (4 + 8) / 2 = 6

	avg += 24;
	std::cout << avg << '\n'; // (4 + 8 + 24) / 3 = 12

	avg += -10;
	std::cout << avg << '\n'; // (4 + 8 + 24 - 10) / 4 = 6.5

	(avg += 6) += 10; // 2 calls chained together
	std::cout << avg << '\n'; // (4 + 8 + 24 - 10 + 6 + 10) / 6 = 7

	Average copy = avg;
	std::cout << copy << '\n';

	return 0;
}

and produce the result:

4
6
12
6.5
7
7

Hint: Remember that int8_t is usually typedef'd as a char, so std::cout treats it accordingly.

Show Solution

2b) Does this class need an explicit copy constructor or assignment operator?

Show Solution

3) Write your own integer array class named IntArray from scratch (do not use std::array or std::vector). Users should pass in the size of the array when it is created, and the array should be dynamically allocated. Use assert statements to guard against bad data. Create any constructors or overloaded operators needed to make the following program operate correctly:

This programs should print:

5 8 2 3 6
5 8 2 3 6

Show Solution

4) Extra credit: This one is a little more tricky. A floating point number is a number with a decimal where the number of digits after the decimal can be variable. A fixed point number is a number with a fractional component where the number of digits in the fractional portion is fixed.

In this quiz, we're going to write a class to implement a fixed point number with two fractional digits (e.g. 12.34, 3.00, or 1278.99). Assume that the range of the class should be -32768.99 to 32767.99, that the fractional component should hold any two digits, that we don't want precision errors, and that we want to conserve space.

4a) What type of member variable(s) do you think we should use to implement our fixed point number with 2 digits after the decimal? (Make sure you read the answer before proceeding with the next questions)

Show Solution

4b) Write a class named FixedPoint2 that implements the recommended solution from the previous question. Provide the overloaded operators and constructors required for the following program to run:

This program should produce the result:

34.56
-0.08
-0.08

Hint: Although it may initially seem like more work initially, it's helpful to store both the non-fractional and fractional parts of the number with the same sign (e.g. both positive if the number is positive, and both negative if the number is negative). This makes doing math much easier later.
Hint: To output your number, first cast it to a double.

Show Solution

4c) Now add a constructor that takes a double.

Hint: You can get the non-fractional part of a double by static casting the double to an integer
Hint: To get the fractional part of a double, you'll first need to zero-out the non-fractional part. Use the integer value to do this.
Hint: You can move a digit from the right of the decimal to the left of the decimal by multiplying by 10. You can move it two digits by multiplying by 100.
Hint: You can round a number (on the left of the decimal) by using the round() function (included in header cmath)

The follow program should run:

This program should produce the result

34.56
-0.08
-0.08
0.01
-0.01
5.01
-5.01

Show Solution

4d) Overload operator==, operator >>, operator- (unary), and operator+ (binary).

The following program should run:

And produce the output:

true
true
true
true
true
true
true
true
-0.48
0.48
Enter a number: 5.678
You entered: 5.68

Hint: Add your two FixedPoint2 together by leveraging the double cast, adding the results, and converting back to a FixedPoint2.
Hint: For operator>>, use your double constructor to create an anonymous object of type FixedPoint2, and assign it to your FixedPoint2 function parameter

Show Solution

10.1 -- Object relationships
Index
9.15 -- Shallow vs. deep copying

88 comments to 9.x — Chapter 9 comprehensive quiz

  • Cunni

    Hi Alex,

    In the final question (4d), you made the ‘+’ overload a friend function. Would it be better to make it a normal function since it doesn’t modify any members? Is there any difference?

  • C++ Learner

    Hi, can you explain what this constructor does(I mean where it is used). When we create an object in main function we only use constructor with one parameter.

  • Omri

    I understand the following:
    Operator overloading is a variant of function overloading that lets you overload operators for your classes.
    as follows:
    Operator overloading is a variant of function overloading that lets you overload language operators, originally designed to be used with language primitives, so that they can be used also directly with user objects (classes types) according to user design. This can make interaction with user objects and between user objects more intuitive and easy.
    Did I get it right?

  • Alexander Kindel

    For question 2a, I initially implemented the overload for += like this:

    I understand that returning by value rather than by reference is bad practice here for efficiency reasons, but I would have expected it to produce the same results as returning by reference, and it doesn’t; in the line of the test program that chains two calls together-the one line where the return value is relevant-I get 6.4, which is what the value would be if the second call were omitted. This suggests to me that for the second call, the program is calling some version of += other than the one I defined, so that the side effect of updating avg doesn’t apply, but I don’t know which version that would be or why.

    • Alex

      This function as you have written it returns a copy of the Average (because it’s returning by value rather than reference), so when further += operators are applied, they’re applied to the copy, not the original. This copy is then discarded, which is why the second call appears to be omitted.

  • Hi, in the solution for question 3 (line 16), what does the { 0 } do?

    Thanks.

  • Gokul

    Hi,Alex! Thanks for the tutorial. It is really great. I am having a doubt. Please consider the following code, where I am trying to add two objects and a double without overloading operator+. which should produce error during compile time, isn’t it??!! but it compiles and runs fine. But if I remove typecast overloading function for double, it throws error as expected during compile time. I am confused. Please help me in understanding what is happening here!!!

    • Alex

      No, no error. When you add a+b, the compiler will first see if there’s an operator+ that takes FixedPoint2 operands. There isn’t, because you haven’t defined one. Next, it will see if it can convert your FixedPoint2 into something that it does know how to add. Because you’ve provided a conversion to double, a and b are getting converted to doubles, which then get added.

      If you remove this conversion function, then the compiler can’t figure out how to convert a and b into anything that can be added, so it gives an error.

  • john

    Hi Alex, I’m having a problem with conversion of FixedPoint2 to double. There seems to be a problem in the function round, which sometimes performs rounding incorrectly. If I enter x.555, sometimes I mostly get incorrect decimal part 0.55 (but it works correctly for 0.555 and 2.555 for example). Any idea what’s the problem?

    • Alex

      Yup, you’re seeing the effect of precision. When you pass in 2.555, you’re actually passing in 2.555000000000000002 (or something like that). This gets rounded up to 2.56. When you pass in 1.555, you’re actually passing in 1.5549999999999999 (or something like that). This gets rounded down to 1.55. So you get a slight inconsistency.

      I’m not sure of a good way to correct for this, and it’s one of the things that makes working with floating point numbers so frustrating.

  • Michiel

    Hey Alex! I’ve recently been working on a new project, and I would like to see if you could give me some advice. Also, there’s something broken in my code, as enemies seem te be able to walk over traps (run the code a couple of times and you’ll see it). Anyway, here is my code (separated in mulitple files).
    point2d.h

    I know it’s a lot but it would really help me out!

  • Matt

    Line 53 in the suggested solution for 4d fails permissive checks with: passing ‘const FixedPoint2’ as ‘this’ argument discards qualifiers.

    • Alex

      That error message essentially means, “the program is violating a const somewhere”, but I don’t see where that’s happening on line 53. fp isn’t const.

      What compiler are you using?

  • Hugh Mungus

    Also, could you take a look at my code? I tried to do as much as possible without looking at the hints.

  • Hugh Mungus

    Hey Alex,

    I really like doing the end of chapter quizzes because they are really challenging. I especially like the struggle of finding an answer and the satisfaction of actually getting a working solution. So I was a little let down when I saw that the fourth quiz had a lot of hints. I understand that the hints help guide us to the right answer, but I would like the option to not see hints in the instruction as it can take away some challenge, and consequently the fun, of the quiz. Could you consider placing a "show hints" button similar to that of the "show solutions" button?

  • Oeleo

    There is a typo at the third sentence : "When operators are overloaded, the intent of the operators should be kept as close to the original intent of operators *as* possible."

  • JoePerkins

    The solution

    doesn’t work if there is a mismatch between signs and the first parameter is not 0.
    For example:

    returns 2.88

  • Surya

    Hi Alex, thanks for your awesome tutorials. so I was doing the 4d problem and ran into this error in the test function.

    error: ambiguous overload for ‘operator==’ (operand types are ‘double’ and ‘FixedPoint2’)

    I tried hard but I couldn’t find where the prob is. pls help me.
    These are my functions.


    friend FixedPoint2 operator+(FixedPoint2 &a, FixedPoint2 &b) {
                return FixedPoint2(static_cast<double>(a) + static_cast<double>(b));
            }


    friend bool operator==(const FixedPoint2 &a, const FixedPoint2 &b) {
                return ((a.m_int == b.m_int) && (a.m_frac == b.m_frac));
            }

    and how exactly should i use the syntax highlighter?
    thanks:)

    • Alex

      Not sure. Can you paste the line that’s generating the error? To use the syntax highlighter: [code]code here[/code]

      • Surya

        all similar lines generate the error.

        • Nathan

          Surya,

          You error is being generated because the compiler doesn’t know whether to treat the LHS and RHS as doubles, and then use operator== for doubles, or to treat them as FixedPoint2’s and use the overloaded operator== for FixedPoint2’s.

          You can fix this by either making your overloaded operator explicit (in which case it will default to evaluating as doubles),
          or to explicitly static_cast your fixedpoint2’s:

          • Surya

            Thanks for saving me from my headache. The second one worked. But I still can’t understand why my operator+ returns a double

            • Nathan

              Your binary+ operator does not return a double, it returns a FixedPoint2.

              Recall that you have an overloaded double casting operator.

              Hence, the operator== has two relevant definitions: one which returns a bool based on the input of two doubles, and one that returns a bool based on the input of two FixedPoint2’s.

              Thus, the compiler sees the == comparison, and is unable to decide (disambiguated) between which operator== to use.  If the compiler chose one arbitrarily, it could make for potentially horrendous debugging problems - so the compiler is nice and instead throws an error.

              This is why the fix is to either make your double cast operator explicit (so then the compiler knows not to treat the as operands doubles), or explicitly cast the operands as FixedPoint2’s.

        • Alex

          Surya, what compiler are you using? I’m unable to reproduce this myself across a few different compilers.

          What Nathan says below is true based on your error message. However, I don’t understand why the compiler would consider the different versions of operator== to have equal precedence -- the one with FixedPoint2 operands should be superior because it requires no conversions, whereas the double version requires two conversions.

  • Chris

    Hi Alex,

    number 4d is so confusing for me haha..

    i think there is a problem with the answer, i just copy paste your answer to codeblock and the output is like this
    false
    true
    false
    true
    false
    true
    false
    true
    -0.48
    0.48
    Enter a number: 5.678
    You entered: 5.68

    and In number 2, Why we need use parenthesis for chaining += operator? When I remove it, the compiler say "lvalue required as left operand of assignment" it doesn’t make sense. I think avg += 6 always evaluate first, so we get avg variable(lvalue) and evaluate avg += 10. Just like chaining i/o operator, we don’t need parenthesis for std::cout << 6 << 10;

    • Alex

      I think this is the same issue/bug that a reader encountered earlier, where GCC is doing something that appears to be a bug. For some reason, operator== is failing to correctly compare two identical FixedPoint2.

      If you run the following code:

      You can breakpoint ref and ref2 and see that they have exactly the same values (m_base=1, m_decimal=98). Yet, for some reason, GCC decides they aren’t the same when they are compared.

      An easy workaround to this issue is to explicitly define an overloaded operator==, so I’ve updated question 4d to include one.

      As for the precedence issue, operator+= operates right to left by default. So if you have “avg += 6 += 10” (no parenthesis), then 6 += 10 evaluates first, and this fails because 6 isn’t an lvalue that can be modified.

      The parenthesis change the order of execution so “(avg += 6)” executes first (returning avg) and allowing (avg += 10) to execute next.

  • Maolin

    Hi Alex,

    one observation from the reference solution for 4c.

    this line:

    if it is broken into 2 lines and cout them:

    the prints are pretty out-of-expectation. (and this == is also evaluated to be true which is really a surprise.)
    the reason for (*) is in the overloaded operator+, the case of borrowing from more significant digit is not considered. as in the example above 0.75 + (-1.23) is computed as following:
    m_base = 0-1 = -1;
    m_decimal = 75 - 23 = 52;
    so that the printed result is simple -1.52, which is wrong.

    the reason for (**) is an output issue. could be fixed by considering the sign of m_decimal in the overloaded operator<<.

    One another question I want to ask is, why the operator== doesn’t have to be overloaded? It seems that an implicit conversion to double is going-on there, but how does it work out exactly?

    many thanks in advance.
    Maolin

    • Alex

      Yup, you’re right. I’ve updated the quiz questions pretty substantively in response to this, leveraging the conversion to double much more heavily than before. This dramatically simplifies the code overall, and so is probably a good thing anyway.

      As for operator==, yes, it’s leveraging an conversion to double in order to do the comparison. The compiler is smart enough to know that it can’t compare FixedPoint2 directly (because we didn’t overload an operator==), so it will check if FixedPoint2 can be converted to any of the types it does know how to compare. Because we’ve defined a double cast, it finds one it can use.

  • Mario

    In the solution of 4a you said:
    "A better solution would be to use a 16-bit signed integer to hold the non-fractional part of the number, and an 8-bit signed integer to hold the fractional component."

    You explicitly mentioned to use a SIGNED 8-bit integer for the fractional part. Would it not be better to have the fractional part as an unsigned integer, because the fractional should not be able to be negative?

    Or am I missing something, if so, please tell me. 🙂

    Regards, Mario

    Edit: I meant 4a, not 4b and I forgot a comma.

    • Alex

      Two thoughts here:
      1) In general, you should avoid unsigned numbers, unless you are doing bitwise operations.
      2) There’s no requirement that the fractional component be non-negative. When I wrote my first solution to this, I thought the same as you did -- the non-fractional component would be positive or negative, and the fractional component would always be positive only. But consider what happens when you add a negative fraction and non-negative fraction together. If the fractional components are always positive, then they don’t add together correctly. Then you have to start looking at the sign of the non-fractional component to determine whether you should add or subtract the non-fractional component. It’s messy and complicated. If you keep the sign of the non-fractional component and the fractional component the same, it makes the math trivial.

      • Mario

        I posted my comment before I solved the quiz completely, therefore I did not see the problem then. I thought that there are two solutions, either keep track if the fractional part is negative with a boolean variable and have the fractional part always positive, or let the fractional part be able to be negative and convert the output accordingly.

        Are there any other, more efficient solutions? Because I think that both of these solutions are somewhat complicated.

        But why should unsigned integers be avoided, except for bitwise operations? I got used to making variables that should not be able to be negative unsigned.

        Regards

        • Alex

          I’m not aware of any more efficient solutions for this kind of thing. Realizing that it’s better to keep the sign of the fractional component the same as the non-fractional component isn’t obvious, but once you realize that the code isn’t too bad.

          I talk about why you should avoid unsigned number in the lesson on fixed width integers.

  • Jie

    I was really confused too.. If it is a complier issue, I don’t think I have to worry about it too much at this stage. Thanks so much for your answer and your tutorial, they are awesome!

  • Jie

    Hi Alex, thanks so much for your tutorial! I have a problem with the last one. Both your code and my code gives false true false true etc.. I tried something like double d1 = FixedPoint2(1, 98); double d2 = FixedPoint2(1, 98); if I do std::cout << (d1 == d2); it is true, but if I do std::cout << (FixedPoint2(1, 98) == FixedPoint2(1, 98)); it gives me false.. and I tried different number for the second one, if I do (1, 75) then it is true, others tries also give me false…Do you know why I get such results? I am using codeblocks 16.01 on windows 10. Let me know if you have any idea, thank you!

    • Alex

      This one has me stumped. It produces the right value in Visual Studio 2015. However, I can duplicate your issue in Code::Blocks on Windows 7. It’s not an issue with FixedPoint2, because the following program has the same issue:

      But the following one works:

      The strangest part is that if you compare decimal with 98.0 inside function foo(), it indicates they’re the same. But when you use decimal, you get a false result, and when you use 98.0, you get a true result.

      This looks to me like some kind of crazy compiler bug, but where or why is eluding me right now.

  • Tomas

    Heey Alex!
    These exercises are great impressive job!

    I have a question.
    Since we put our fraction part as an int8_t it is still possible for a value to be more than 99 and less than -99 maybe it would be a good idea to iplement this code?

    • Tomas

      This is what i meant in the 2nd part

    • Tomas

      Oh my bad I saw that it was used in 4c 🙂

      • Alex

        That was only for adding two FixedPoint2 together (when you could run into this situation normally). Without addressing the issue in the constructor, you could still create an invalid FixedPoint2 in the first place.

    • Alex

      You’re probably right, this could happen, even though it’s a misuse of the class. I’ve added a comment to the code indicating that we should handle those cases in the constructor.

      [Edited to remove misleading comment]

      • Tomas

        But if we are using int8_t isnt the range -127/128?

        • James

          Dear both,

          Alex’s code now passes all of Sharjeel Safdar’s tests and his own tests. Remarkably, none of these tests handles the case where the sum of the decimal parts overflows an 8-bit integer, e.g.,

          which, by chance, was the first test I ran, and is certainly not a misuse of the class. This passes all tests:

          I guess it works because my compiler (gcc (Ubuntu 5.4.0-6ubuntu1~16.04.4)) doesn’t know of any

          and does an implicit conversion from int8_t to int somewhere.

          Now I’ve got this far, I realise this may the point Tomas was making 🙂

          Best,
          James

          • Alex

            Yes, you (or Tomas) found a flaw in the original solution -- adding two m_decimal and assigning the result back to an int8_t variable could result in overflow, and the test cases didn’t uncover this possibility.

            Your solution is better, as it avoids overflow by leveraging the fact that adding two int8_t together results in integral promotion to an integer and adjusting back down into range before creating a new decimal.

            I was going to use your code for the updated solution, but I realized all three of your cases could be collapsed into two statements with no branching. Check it the updated quiz solution, you’ll probably find it interesting.

            • James

              Ah… I was trying to come up with something that used the sign of the sum, before I came up with the above. All of my attempts were ugly (and didn’t work). Thanks for sharing.

        • Alex

          Yes, of course. Not sure what I was thinking yesterday.

  • Christos

    Hi Alex,

       I added a few lines of code inside quiz 3, as live annotation to show the steps in the order executed.
       I would like to ask about the execution of "return" command of the fillArray function. When executed the "return a;" command inside the fillArray, this calls the copy constructor to construct and initialize "a" object of the main function. Is there any intermediate copy step through the fillArray() caller?
       I would like to understand the background steps from the point after the "return a;" has been executed, until the complete of  the command line "IntArray a = fillArray();".

    Thank you,
    Christos

    • Alex

      > When executed the “return a;” command inside the fillArray, this calls the copy constructor to construct and initialize “a” object of the main function. Is there any intermediate copy step through the fillArray() caller?

      This _may_ call the copy constructor. If it does, main::a is the primary object, and fillArray::a is the copy constructor parameter. The copy constructor handles doing the copy, and that’s all that happens (there’s no other steps).

      However, the copy may also be elided, which is what I see in Visual Studio 2015. That may or may not be the case for your compiler.

  • Matt

    In your solution for quiz# 3, in your friend function, I think "std:cout" should be "out".

    Also, this is the first time I noticed that you defined a friend function within the class that it’s declared. Was that a mistake, or is it acceptable to do it that way?

    Also, within the same solution, in the constructor, I noticed that you used the assert function differently than had been taught earlier(to my recollection at least). It’s not difficut to figure it out, but I still think a comment in the code would be helpful too.

    • Alex

      1) You’re correct, I’ve fixed the example.
      2) I did it that way for conciseness, you’re better off putting your functions in a .cpp file. I updated the lesson on overloading operators as friend functions to note that you can do this.
      3) That’s because I made a mistake. I’ve updated the example. I’ll update the lesson on assert to explain this as well, because it can be useful.

  • Alex

    I see where your confusion is. You should probably re-review lesson 8.5b -- Non-static member initialization.

    Classes can initialize members in one of two ways: either by using non-static member initialization in the member definition, or using a member initialization list in the constructor.

    If you look at the Average class, it uses non-static member initialization to initialize its members. The default constructor doesn’t do any additional initialization of its own.

    Therefore, if you remove the non-static member initializers and then instantiate an object, m_sum and m_count never get initialized (because the constructor isn’t doing it).

    If you add a member initialization list to the Average constructor, then it will initialize the members.

  • jered

    in question 2, why does the output yield massive numbers if you leave the member variables uninitialized?

    i.e.
    int32_t m_sum;
    int8_t m_count;

    as opposed to:
    int32_t m_sum=0;
    int8_t m_count=0;

    • Alex

      C++ doesn’t do initialization of fundamental types if you don’t assign an initializer. Therefore, when an uninitialized variable is assigned memory, that memory will still have whatever garbage value was in there previously. If you print this to the console, then you’ll print this garbage value (as an integer).

      • jered

        Hi Alex,

        But doesn’t the class use a constructor that utilizes memberwise initialization?

        Oddly, it is only when I provide a constructor that uses memberwise initialization that I am able to get the values to print desired results (while using uninitialized member variables).

  • sigil

    1) Okay, I see your point. Depending on the implementation of both conversion and operator+, there may be cases where your solution is more efficient.
    2) Still not sure what you mean? We are passing the index by value, it’s a copy. Would not be able to change the original even if we wanted to.
    3) Thanks for the reminder not to expect others to have static analysis tools. *smile* You are correct here. It’s a C relic compilers would let slip.

    • Alex

      For #2, it’s true you can’t change the value of the argument, but you could inadvertently change the value of the parameter. In this case, making the parameter const disallows changing the parameter, so we can guarantee that whatever our function does, the parameter will always be equal to the value of the argument, not inadvertently changed to some other value.

      It’s just a minor tool to help us not make mistakes.

  • sigil

    First of all, thank you very much for your quite comprehensive tutorials. I’m renewing my memories of C++ proper after a long detour into C++/CLI, and your site really helps. A few comments on this particular part, if I may:

    1. (2c) does not require an overloaded binary operator+. The

    and

    expressions work okay by converting the arguments to doubles. Just comment the operator+ out; output will not change. Further still, we can use doubles for math to simplify this operator greatly if we decide to keep it.

    2. In (3), the const qualifier in the

    line seems to be redundant.

    3. In (3) again, wouldn’t it be more natural to use an unsigned int for array indices? Would save us the negative value checks, even if in the debug version only.

    • Alex

      A few thoughts:
      1) You’re right, it’s not required. But writing one is good practice, and it’s probably more efficient as well. If we rely on the double conversion then we have to convert two FixedPoint2 into anonymous doubles, and then convert them back to a FixedPoint2 to do a single addition. With an overloaded operator+, we can avoid the extraneous conversions and just deal with a temporary FixedPoint2 to return the result.
      2) It’s not redundant. It helps ensure we don’t accidentally change the value of index.
      3) No. If you use unsigned integers and someone passes in a negative number like -1, C++ will implicitly convert it to a very large integer, and you’ll end up walking off the end of your array, and the OS will probably shut down your program. With signed integers, we can detect if someone tries to do this and handle it. I talk a little bit about this in lesson 2.4a.

  • Sharjeel Safdar

    Hi, Alex!

    I think there is some problem with your code for the overloaded operator+ in Quiz 2c.

    I have written the following code for overloaded opertor+:

    I’m very bad at comments. 🙁

    I wrote the following test code for this function to test all the control paths:

    This test code gave me the results as expected:
    true
    true
    true
    true
    true
    true
    true
    true

    After this I saw your code in the solution to quiz:

    I tested your code with my test function which gave me the results:

    true
    true
    false
    false
    true
    true
    false
    false

    It seems that your code couldn’t handle the cases when both FixedPoint2 objects are negative and when the first object is negative and the second one is positive. I hope you will take a look at my code. I know that my code contains a lot of redundant code; so I hope you will also improve mine in that aspect.

    Also, the constructor in your code:

    Here, the 7th line should be

    Because without the abs() function the m_decimal part of a negative fixed number will also be negative (e.g. for -5.892: m_base = -5 and m_decimal = -89)

    Still, we love your tutorials and your magnificent efforts.

    (I use Visual Studio 2015 Community IDE.)

    • Alex

      You are indeed correct. I didn’t adequately test my code in all cases.

      So, a few changes:
      1) I’ve fixed the code per your suggestions.
      2) This program has gotten complicated enough that I’ve moved it from question #2 to question #4 -- it is now extra credit. I’ll create a new #2 as soon as I think of something good.
      3) I hope you don’t mind, but I integrated your test function into the quiz question and answer. I like how it shows a way to test your code and goes through all the various permutations. Well done!

      • Sharjeel Safdar

        Thank you, Alex. 🙂
        I have learnt all the things (I know) from your website.

        • Alex

          I realized today that this program can be simplified if we let the fractional component have the same sign as the non-fractional component. I’ve updated the example. Check out how much more straightforward the operator+ is now.

          • Sharjeel Safdar

            You are right.

            • Hey, this thing again. To solve this I had to flag for overflows with different sign values. The solution you present on the view solution yields

              true
              true
              true
              true
              true
              false
              true
              false

              I solved it thus:

              I admit it’s clunky., but it does single out the cases where it didn’t work before(and neither does the solution) - overflow with different signs - and fixes them.

              If your solution works for you then I wonder why it doesn’t for me.

              Thank you very much for this website. It’s been quite a ride through C++. I’m going into lesson 10 now!

              • Alex

                I just recompile the solution presented in the lesson and I got all trues. That’s interesting that you didn’t -- perhaps I did something I shouldn’t have (that just happens to work in Visual Studio)?

                With the reference solution, you should never get overflow if the two numbers have opposite signs. Consider: In the reference implementation, the decimal has the same sign as the main number, so if the two numbers have opposite signs, the decimal will have opposite signs. For the positive number, the decimal will be between 0 and 99. For the negative number, the decimal will be between -99 and 0. If you add the two together, you can never get a number less than -99 or greater than 99. Thus, no overflow possible.

                I did realize that I’d made a small mistake in the test cases (that didn’t matter for my reference solution, but may matter for other solutions), so I updated the bottom 4 cases. But that should be irrelevant for the reference solution and this discussion.

                • Thanks for the reply.

                  I just copied your solution and got all trues. Then I copied just your solution for the operator+ function (which was what I did before), thinking there was something wrong elsewhere and… got all trues.

                  I can’t really explain it. I’m sorry to have put you out. The code works and is much simpler than mine, which is byzantine. I was getting to a headspace of frustration and so I did that tailor-made solution for my problem. I think if I’d stopped and returned later with fresh eyes, I’d have come up with a solution close to yours.

                  I hope I would anyway.

                  Thanks a bunch.

      • Nivash

        Hi Alex,

        Since there is double cast support, would you advise below code for operator+ implementation? I feel this looks cleaner and less error prone.

  • Matthew senko

    Where is array.m_length coming from?

    • Alex

      This is a copy constructor, so we’re going to make a copy of an IntArray from an existing IntArray. That existing IntArray is being passed in as parameter array. Because array is an IntArray, it has a m_length and an m_array member. We can access them directly since the constructor is a member of IntArray.

      I’m not sure I ever mentioned that last part in the Copy Constructor lesson. I’ll add it.

  • Matthew senko

    I couldn’t get through either question by myself, I had to look at the solution. I understand the concepts I just don’t know how to utilize them. What do you recommend I do? Should I continue on with the proceeding chapters?

    • Alex

      A couple of thoughts:
      1) These exercises aren’t easy. They’re designed to make you think and experiment a little bit. Even if you didn’t get the right answer yourself, make sure you understand how the programs work.
      2) You can practice by creating some exercises for yourself. Think up a use case where it would be useful to implement overloaded operators and write a simple program that uses them.
      3) You should continue onward. Often concepts will get reinforced later, which will give you additional context to understand them.

      That said, I’d love some feedback on where you got stuck in both cases. Maybe some additional hints would help future readers.

  • Anddo

    Typo in the solution of 2c)

    One notice, the output of solution of 2c) was, specifically,

    44.17
    24.95

    While they should be (by calculation, I’m sure of the second not the first)

    44.18
    24.94

    When I tried to debug to understand why this result comes out, I saw that int8_t handled as char and couldn’t show the actual number. Anyway, I used int16_t for decimal and all worked as expected. If you have explanation for this, I really would like to hear it from you since I did NOT fully understood what went wrong (from math calculation point of view) when int8_t is used.

    Edit: After success of trying int16_t for the decimal part, I tried back int8_t and guess what !? I got the correct results

    44.18
    24.94

    There is something I don’t get here !

    • Alex

      I’m not sure I understand what your question was.

      But you’re right, the results of the quiz were wrong. I changed the requirements from truncate to round (otherwise dealing with precision issues is a nightmare).

      That said, I wouldn’t be surprised if there was an x.5 floating point value that was represented internally as x.49999 and gets rounded down instead of up.

      A better idea is probably to disallow use of double input altogether and only allow the user to input strings.

      • Anddo

        Sorry I made it not clear.

        "That said, I wouldn’t be surprised if there was an x.5 floating point value that was represented internally as x.49999 and gets rounded down instead of up."

        Seems that what happened. The results you presented at first were the same output I got using your solution !! on Visual Studio 2015 and on XCode. However after using int16_t instead of int8_t, the results got out differently (correct though). After using int8_t again as in the original solution you presented, the output stayed correct unlike the first time I used int8_t as in the solution. My question if you can help me with explanation for this. And you’re right, x.5 floating point value represented internally as x.49999 could cause such result. Thank you for your solution about input string, it appears it is the best really.

        Kinda question out of the subject, there are many calculation applications dealing with such numbers, do you think it’s the best approach to handle floating numbers as strings or integers (like in this quiz by making numbers after the dot integers) ? Or there are better mmmmmm techniques ?

        Again thank you 🙂

  • hachiroku

    I ended up with a simple solution for operator+() in quiz 2c by using the overloaded double typecast.

    You’ll need to return m_decimal as negative in operator-() and handle this correctly in operator<<() for the program to work properly.

    However, using this code in operator+() forces you to make the FixedPoint2 parameters non-const, which I don’t understand since you’re still not changing their values.

    Is it possible to make the overloaded double typecast handle const parameters? And is there any other flaw with using this solution?

  • Yaboi

    In the solutions of question 2 you never declared a type for static_cast, is this a feature of certained compilers to automatically know the right type? (I couldn’t find anything online and visual studio 13 doesn’t seem to support it).

    In this line:

    Wouldnt it be more efficient to call the move constructor/assignment or is the copy constructor just as fast/justifiable?

    Also, in 2c when overloading the >> operator for cin, when i type in a small value like 5.01, it automatically floors to 5.00. Does this have something to do with precision errors/roundings? All other values i have tested seem to work correctly.

    Lastly if I don’t put const in front of some of the arguments (for example the copy constructor) the compiler completly ignores the constructor and seems to create his own. Is this just because the compiler demands the copying to not modify the original object?

    Another thing: The code in the solutions isn’t formated in a very readable way (html/css) like the other code in your other tutorials. I’m not trying to bash these tutorials, because I find them extremely helpful, far more friendly for people new to the language and they have helped me a lot with learning the language, just trying to give my feedback 🙂

    • Alex

      Some of the quiz questions had a typo that was causing the code to be treated as HTML rather than C++. This is why the type of the static cast got hidden, as well as the formatting being messed up. Both should be fixed now, thanks for pointing that out.

      The code in the quiz question is pretty inefficient -- it’s there more to ensure you’ve learned certain concepts than to be a model for how to write great code. If I were writing that code to be efficient, I’d have main pass in an IntArray by reference and have the function fill it out, thus avoiding any copies.

      The problem with the value 5.01 is that that particular value is being stored as 5.0099999…, so it’s getting truncated. Precision errors strike again! I’ll have to find a better solution for this question.

      If you don’t put a const in front of a reference parameter, then that parameter won’t match any literals or anonymous objects. The return value from fillArray() probably qualifies.

Leave a Comment

Put C++ code inside [code][/code] tags to use the syntax highlighter