Your playground

Let’s solve some exercises to implement structures for the rest of the arithmetic operations. Again, you’ll write the code in the playground given below. The add() function is already appended to this widget, meaning the code is present but hidden so that you can use all the helper functions by just calling them. You don’t need to write their entire code.

Press + to interact
#include <iostream>
using namespace std;
struct MixedFraction
{
int sign; // either will be +1 or -1
int w;
int n;
int d;
};
//Helper function already appended
int gcd(int a, int b);
void improperFraction(MixedFraction &frac);
void sameDenominator(MixedFraction &L, MixedFraction &R);
void computeSign(MixedFraction & frac);
void mixedFraction(MixedFraction &frac);
MixedFraction add(MixedFraction L, MixedFraction R);
void print(string msg, MixedFraction frac);
// The four prototypes for the addition, subtraction, multiplication and division
MixedFraction add(MixedFraction L, MixedFraction R); // Already imeplemented
MixedFraction sub(MixedFraction L, MixedFraction R);
MixedFraction mul(MixedFraction L, MixedFraction R);
MixedFraction div(MixedFraction L, MixedFraction R);
int main() {
MixedFraction final= {}; //final fraction
MixedFraction left = {+1, 2, 1, 2}; //left fraction
MixedFraction right = {-1, 2, 1, 2}; //right fraction
print ("Left", left);
print ("Right", right);
final = add(left,right); // f =L+R
print ("Left + Right", final);
final = sub(left,right); // f =L-R
print ("Left - Right", final);
final = mul(left,right); // f =L*R
print ("Left x Right", final);
final = div(left,right); // f =L/R
print ("Left / Right", final);
return 0;
}
//Add function implementation already appeneded
MixedFraction sub(MixedFraction L, MixedFraction R)
{
MixedFraction result{};
// Write your code here
return result;
}
MixedFraction mul(MixedFraction L, MixedFraction R)
{
MixedFraction result{};
// Write your code here
return result;
}
MixedFraction div(MixedFraction L, MixedFraction R)
{
MixedFraction result{};
// Write your code here
return result;
}

Exercise 1: Implementing the sub() function

Write the sub() function and uncomment the lines in main() where sub() is called. If you have trouble getting to the solution, you can click the “Show Hint” button to see how to implement it.

Exercise 2: Implementing the mul() function

Write the mul() function and uncomment the lines in main() where mul() is called. If you have trouble getting to the solution, you can click the “Show Hint” button to see how to implement it.

Exercise 3: Implementing the div() function

Write the div() function and uncomment the lines in main() where div() is called. If you have trouble getting to the solution, you can click the “Show Hint” button to see how to implement it.

Remember: We’re updating the parameters received, L and R, multiple times in several functions, such as improperFraction(L), improperFraction(R), and sameDenominator(L,R). These functions receive parameters by reference and update them. However, the changes made by these functions will not affect the fractions passed as arguments from the main() function. This is because the add(), sub(), mul(), or div() functions receive parameters by value. Consequently, any changes made after entering these functions will only impact the local copies of the parameters, not the arguments passed in the callee positions.

Takeaways and issues with the structured approach

Utilizing structuring, which involves grouping similar values as user-defined data types, has several advantages, including:

  1. Ability to return multiple values: By utilizing structures to store multiple variables of different data types as their members, we can easily return multiple values, reducing code complexity and eliminating the need for passing variables by reference.

  2. Data abstraction: Structures enable us to introduce data abstraction. For example, rather than associating three variables for a mixed fraction, we now have a single structure called a mixed fraction. This structure encapsulates them as three-member variables, meaning the user doesn’t need to keep track of every single variable for each mixed fraction and now has an entity that represents a mixed fraction for them. We’ll discuss this concept in later lessons.

  3. Reduced complexity and code size: By comparing the previous code that used primitive data types and the current one that utilizes struct from C++, we observe a substantial reduction in code size and complexity. Introducing data grouping through struct enhances code management, resulting in a more modular and comprehensible codebase.

Note: Keep in mind that while structures can be used to return multiple values, it’s not their primary intended purpose, and using them solely for that may not be considered a good programming practice. Instead, reference type parameters should be preferable for this purpose. Structures, on the other hand, excel when dealing with a collection of related data that should be logically grouped together, as was the case in the MixedFraction case study. By leveraging structures in such scenarios, we achieve code organization and readability, making our code more robust and maintainable.

Despite the significant improvements we observed in our case study by introducing struct within the procedural/structured programming paradigm, this approach also has some drawbacks:

  1. All public members: Structure members can be accessed and modified throughout the code’s scope. Consider the scenario of creating a timer clock that increments every second. For this purpose, a clock structure variable is created of a timer structure with three members: seconds, minutes, and hours. We utilize a built-in library function to increment this timer, which should run independently in the background. However, what if someone unintentionally writes the command clock.hours = -10 at some point? This would update the hours member to an incorrect value of -10. It becomes evident that we need a way to restrict unchecked access to structure members. In a future lesson, we’ll explore a paradigm shift that addresses this issue.

  2. Independent functionality: Notice how a structure is nothing but a user-defined data type, a group of variables representing or associating with a certain category. For example, in the MixedFraction structure of our case study, all its members contain the data of a mixed fraction. To make a functioning calculator, we make separate functions, add(), sub(), mul(), and div(), which reside outside the structure as independent functions. These functions and data are still scattered, and the complexity is still too much as they both grow independently.

Takeaway question

What if we take it a step further and enhance the code’s modularity? Can we introduce a concept where the structure not only stores data but also encapsulates its functionality? The idea is to integrate related data and its associated functionality within a module, creating a more cohesive collaboration. This approach ensures that each module understands the scope of the data it contains and the functionality it offers. This concept marks the beginning of our new paradigm known as object-oriented programming, which we’ll explore in the upcoming lessons.