• Beginner C++: Calculator
    38 replies, posted
Hi, I've been lurking around here for quite a while now and I decided I should get back into coding. You see when I was pretty young, maybe 9 or 10 my dad taught me the basics of BBC Basic. I've tried many other languages, but have given up after a certain point. This time I am determined to stay at it and maybe after a few years begin to develop my own games. To start off, I've read a few internet crash courses on C++ and found it to be a very similar language to a lot of the others. I've done a very simple, but messy calculator script and it works quite smoothly. This was originally just to test functions in C++, but I want to try to gain a little more out of it. So I was just wondering if anyone would be kind enough to help me optimise this script so I can further my learning. [code] #include <iostream> using namespace std; void myfunc(int a, int b, char op); void endfunc(); int main() { int a, b; char op; start: cout << "Please enter an operation(/ * - +) followed by two numbers: "; cin >> op; cin >> a; cin >> b; myfunc(a, b, op); goto start; } void endfunc() { system("pause"); cout << endl << endl << endl; int main(); } void myfunc(int a, int b, char op) { switch(op) { case '+': cout << a << op << b << "= " << a+b << endl; endfunc(); break; case '-': cout << a << op << b << "= " << a-b << endl; endfunc(); break; case '/': cout << a << op << b << "= " << a/b << endl; endfunc(); break; case '*': cout << a << op << b << "= " << a*b << endl; endfunc(); break; default: cout << "I'm sorry, but that isn't an operator. Please try again."; endfunc(); } } [/code] The part I really don't like is the cases, they seem extremely messy and I'm sure there is a quicker/better way to do them. Also keep in mind that I've been coding in C++ for less than a week on and off. Thanks for any help you may give me. Sorry for my bad English!
It really depends on how much you want to optimise that, but one thing is for sure: do [B]not[/B] use system("pause"). The system function forks a new process, and this uses way more memory and CPU time than you really want. Secondly, it only works on OS's with a "pause" in-built shell command or a pause program somewhere accessible, and it's not even guaranteed to work like "pause" in Windows. You can use cin.get() to wait for a single character from the console. Your main function is really spaghetti code at its best. Recursion, labels, and a goto that is never reached... For one - there's no reason to use goto there instead of a loop. And there's no reason to recursively call main. There is no way to make recursively calling main an optmized tail call, so it means your program will use more and more memory the more the user uses the program.
[QUOTE=jA_cOp;16070915]It really depends on how much you want to optimise that, but one thing is for sure: do [B]not[/B] use system("pause"). The system function forks a new process, and this uses way more memory and CPU time than you really want. Secondly, it only works on OS's with a "pause" in-built shell command or a pause program somewhere accessible, and it's not even guaranteed to work like "pause" in Windows. You can use cin.get() to wait for a single character from the console.[/QUOTE] Ah, thank you. I shall change my pauses. I was thinking to optimise it by instead of having all those cases, it just changes the operator you typed in to the operator in use if you get what I mean. So say if you typed in + It would just use + in the cout line.
C++ has no such feature. C++ is a compiled language, you can't just trivially compile code on the fly based on input.
So would my cases be an optimised way to do it. They seem really cheap to do.
You should worry more about the other parts I pointed out.
I completely removed the press any key system and changed the goto to a do-while loop. [code]#include <iostream> using namespace std; void myfunc(int a, int b, char op); void endfunc(); int main() { int a, b, loop; char op; loop=1; do { cout << "Please enter an operation(/ * - +) followed by two numbers: "; cin >> op; cin >> a; cin >> b; myfunc(a, b, op); cout << "\n\n\n"; } while(loop==1); } void myfunc(int a, int b, char op) { switch(op) { case '+': cout << a << op << b << "= " << a+b; break; case '-': cout << a << op << b << "= " << a-b; break; case '/': cout << a << op << b << "= " << a/b; break; case '*': cout << a << op << b << "= " << a*b; break; default: cout << "I'm sorry, but that isn't an operator. Please try again."; } } [/code] Is this better? I'm glad you told me to get rid of the goto, it helped me understand the use of the do-while loop! Another quick question, is "endl" better or worse than using "\n" other than the size differences?
That is a lot better! Notice how your code got both shorter and more readable. (in such a small program, this is much more important than the fact that you're saving a bunch of cycles this way) Notice that you have a conditional in your loop, but you're never actually changing "loop", so the loop with go on forever. This is kind of confusing, when what you wanted to represent was really an infinite loop. In both C and C++, you can do explicit infinite loops (the 'correct' way) like this: [cpp] for(;;) /* for loop with empty expressions */ { /*loop body, note that it is a good habit to indent code inside loops just like functions and conditionals*/ } [/cpp] Another common way that works in both C and C++: [cpp] while(1) { /*loop body*/ } [/cpp] But it's still a little ambiguous because some people might not be familiar with how C/C++ treats integers in conditionals, because they're used to booleans. So, in C++ and also in C since the 1999 standard, you can do: [cpp] while(true) { /*loop body*/ } [/cpp] Which I can imagine being the easiest one to understand as an infinite loop.
Ah! Thank you so much, I've already learned quite a lot that I didn't know before in this thread!
Also, try to think of a way to handle division by zero so that your program doesn't crash when it happens. And to add to what ja_cop said, you can add the following after cout << "\n\n\n"; [cpp] cout<<"Enter -1 to stop the program...1 to continue"; cin>>loop; [/cpp] Now you have a way to optionally stop the program by changing the value of loop, causing the program to leave the loop and end (or just keep it as it is and press the x button :P However, you learn something for future use by doing it this way) [editline]12:08PM[/editline] P.S. Your switch-case is fine, you did it the proper way. Remove the endfunc(); declaration at the top since it's useless now. Rename myFunc to something that describes what the function does such as "performOperation" (remember to rename all instances of myFunc, not just the top one :P). Also, use camelCase, neater coding style (first word in a name is lower case and the rest are capitalised justLikeThis)
[QUOTE=gilly_54;16071673]Also, use camelCase, neater coding style (first word in a name is lower case and the rest are capitalised justLikeThis)[/QUOTE] He can use whatever coding style he wants. The C and C++ standard libraries use all lowercase - so why can't he if he wants to?
This is the code currently: [code]#include <iostream> using namespace std; void domath(int a, int b, char op); int main() { int a, b; char op; while(true){ cout << "Operations: \n + \n - \n / \n * \n Enter an x to quit."; cout << "Please enter an operation followed by two numbers: "; cin >> op; if(op=='x') return 0; cin >> a; cin >> b; domath(a, b, op); cout << "\n\n\n"; } } void domath(int a, int b, char op) { switch(op) { case '+': cout << a << op << b << "= " << a+b; break; case '-': cout << a << op << b << "= " << a-b; break; case '/': if(b==0) { cout << "You cannot divide by zero."; break; } cout << a << op << b << "= " << a/b; break; case '*': cout << a << op << b << "= " << a*b; break; default: cout << "I'm sorry, but that isn't an operator. Please try again."; } } [/code] [quote]Also, use camelCase, neater coding style (first word in a name is lower case and the rest are capitalised justLikeThis[/quote] Doing variables in anything but lower case makes me shaky as I know I'll forget to capitalise it sometime and that just adds onto the time spent debugging it! I can't thank you guys enough for all the help you've given me so far!
I don't know why I prefer while loops over do-while so the code looks way better to me now :P As for the camel case, to each his own, as long as you stay consistent with it throughout your programs! I just noticed one more issue in your program, you should work with doubles or floats with division so that you get accurate answers, "int" doesn't store decimals, just the first number (try dividing 2/3 in your program, you'd get 0). So try making a and b "double" types rather than "int" (everywhere, in your function headers too!) for now and your program is more or less complete :) For your next program, try making a converter. Celsius-Fahrenheit-Kelvin, weights, distances, or a bunch of them together :)
1) Use proper indentation. Your indentation is really fucked up (I'm not sure if you made it that way, or if the forum code tags are fucking it up). Indent every block (a block stuff inside curly { } braces) with a single tab. 2) Declare variables as soon as they are used. It makes things a lot easier to read: [code] char op; std::cin >> op; int a, b; std::cin >> a >> b; doMath(a, b, op); [/code]
[QUOTE=nullsquared;16075040]2) Declare variables as soon as they are used. It makes things a lot easier to read:[/QUOTE] As a C programmer, that statement makes me cringe.
[QUOTE=ROBO_DONUT;16075468]As a C programmer, that statement makes me cringe.[/QUOTE] It doesn't matter, he's using C++. C++ is not C.
[QUOTE=nullsquared;16075510]It doesn't matter, he's using C++. C++ is not C.[/QUOTE] It should at least be pointed out that C89 requires that you place all variable declarations at the beginning. You don't really want to get into the habit of placing your declarations haphazardly without understanding that it only works in C++.
[QUOTE=ROBO_DONUT;16075578]It should at least be pointed out that C89 requires that you place all variable declarations at the beginning. You don't really want to get into that habit without understanding that it only applies to C++.[/QUOTE] There is a reason C99 changed this. Local variables are still created at the beginning of a scope both in C and C++, just that now C also supports sorting this automatically for you. Declaring local variables close to where you're using them usually makes a lot of sense.
[QUOTE=ROBO_DONUT;16075578]It should at least be pointed out that C89 requires that you place all variable declarations at the beginning. You don't really want to get into the habit of placing your declarations haphazardly without understanding that it only works in C++.[/QUOTE] No, it [b]shouldn't[/b] be pointed out. [b]The OP is not using C.[/b]
[QUOTE=jA_cOp;16075662]There is a reason C99 changed this.[/QUOTE] C99 still isn't widely supported. GCC pretty much supports it, but it also allows you to define variables pretty much anywhere (unless you run it with the -pedantic flag). MSVC does [I]not[/I] support C99, and it does not allow you to declare variables anywhere except the beginning of the function when compiling C code. I'm sure there's tons of C compilers for little embedded systems and such that would totally flip out if you tried to put your variable declarations anywhere other than the beginning of the code block. [QUOTE=nullsquared;16075761]No, it [b]shouldn't[/b] be pointed out. [b]The OP is not using C.[/b][/QUOTE] But he might. Eventually.
Looks great. Better than me when I was new. Hope you keep at it.
[QUOTE=ROBO_DONUT;16075809] But he might. Eventually.[/QUOTE] Yeah... don't see that happening...
[QUOTE=ROBO_DONUT;16075809]C99 still isn't widely supported. GCC pretty much supports it, but it also allows you to define variables pretty much anywhere (unless you run it with the -pedantic flag). Visual C++ does [I]not[/I] support C99, and it does not allow you to declare variables anywhere except the beginning of the function when compiling C code. I'm sure there's tons of C compilers for little embedded systems and such that would totally flip out if you tried to put your variable declarations anywhere other than the beginning of the code block. He might, eventually, and if he does, your little "style tip" is just going to cause a big headache.[/QUOTE] Sure - compile with -pedantic and refactor your code when you port it to your Arduino or whatever. By the time he's actually competent enough with C to program for an embedded system he'll probably know what the error means - that he's using a C99 specific feature. (in GCC, errors produced by -pedantic tend to be very good!) OR, of course, he could take the "style tip" and develop a modern, more logical style of coding that can easily be transferred to other languages... Languages like the one he's programming in right now, which happens to be C++.
[QUOTE=ROBO_DONUT;16075809] But he might. Eventually.[/QUOTE] When he does, he'll tell us, and that'll be your cue to come in and help out.
[QUOTE=jA_cOp;16075965]Sure - compile with [b]-pedantic[/b] and refactor your code when you port it to your Arduino or whatever. By the time he's actually competent enough with C to program for an embedded system he'll probably know what the error means - that he's using a C99 specific feature. (in GCC, errors produced by -pedantic tend to be very good!) OR, of course, he could take the "style tip" and develop a modern, more logical style of coding that can easily be transferred to other languages... Languages like the one he's programming in right now, which happens to be C++.[/QUOTE] Oh god, let me give that a whirl.
[QUOTE=ROBO_DONUT;16075809]But he might. Eventually.[/QUOTE] Yeah, and he might also learn Haskell eventually. Should we give him a tutorial on that too?
Lol, this is such a small thing to argue about. I'm used to declaring my variables at the top and I think it's all to do with preference how you want to place them.
[QUOTE=TheHand;16081327]Lol, this is such a small thing to argue about.[/QUOTE] Welcome to the programming section.
I would say at the top is best, but nullsquared did make portalized on the other hand so I don't wanna argue with his programming knowledge
Scott Meyers' Effective C++ has an entry on this (Item 26); Not only does defining variables when you use them increase clarity by providing context, but it's also a potential performance increase for class variables with a non-trivial constructor when you define the variable inside a deeper scope, or past certain points in your function (return statements, throw statements, breaks, etc.) Basically you want to delay variable definition as much as you can.
Sorry, you need to Log In to post a reply to this thread.