Sign in to follow this  

Looking for particular tips on good programming practice

This topic is 3298 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

VC++2008, Direct3D9 Alright, I have the damn triangle on the screen now, good for me. But the more I write, the more I remember the hundreds of good things I could be doing... that I have completely forgotten. The back of my mind is vaguely screaming about these wrongs I'm committing while the front is just trying to learn like a person who doesn't know they exist. So here are some questions on the best ways to go about things. No need to answer all of them... bit of a wordy post this. 1. Some of the guides and tutes I'm looking at are declaring variables and objects right before they need to use them. For example: HWND hWindow = CreateWindow(blah blah blah); That feels a bit spontaneous to me, but of course makes sense when asked 'Why declare something until you know you need it?'. So do people advise declaring objects as much as possible at the top of their scope, or as close as possible to the point where they're needed? 2. Is it a good or bad thing to define a struct in a header? Is it a good or bad thing to have one header that includes practically everything and his dog, and all other files just include that header? 3. One quite recently made tute uses: delete [] myApp; It does this after calling a CleanUp() function to release the D3D objects floating around. I don't think I've used delete [] before. Why has this tutorial done this when the destructor is going to be called automatically anyway? Do you advise using delete [] explicitly? Is it not clearer to instead call the destructor explicitly? 4. In your WinMain(), what exactly is the point of return 1; ? And I've seen different examples, so is it: return 1; or return 0; or return wParam.whateverishotatthetime; ? 5. (Declaring and initialising.) Is it always advised to intialise (to null or 0 if need be) immediately after declaring a variable, instead of waiting until a time you need to assign something to it? To make it more complicated, I remember there being a difference between initialising on the same line as declaration, and initialising after the declaration is complete. Anyone know what I'm talking about and can point out when this difference matters? 6. (Sight between files.) When I use multiple .cpp files, the IDE is automatically making sure the compiler knows it has to include them all. Okay. And including a header is the same as pasting all the text from that header into the file that is including it. Okay. But can someone please give me some clarification on what "sight" there is between different .cpp files? I'm guessing/assuming that something declared at file scope (outside all the {} braces) is a global and can be seen from anywhere in any of the .cpp files. Correct? So it isn't possible to declare a variable that is only visible to the file it was declared in without actually putting it inside some braces. Right? 7. Could someone please simply recommend a good site that clearly explains all things C++. I imagine there's a popular one or two. (I really want to get into good const practice before I go much further.) That's all I can remember for now. Any other general helpful tips you think might fit with the above questions are welcome too. Cheers.

Share this post


Link to post
Share on other sites
Not sure I'm the best person to be replying to a best practices post, but let's try [grin]

Quote:
Original post by Defend
VC++2008, Direct3D9
Alright, I have the damn triangle on the screen now, good for me. But the more I write, the more I remember the hundreds of good things I could be doing... that I have completely forgotten. The back of my mind is vaguely screaming about these wrongs I'm committing while the front is just trying to learn like a person who doesn't know they exist.


While it's good to realize that, it's good to focus on goals. If your goal is to learn, focus on learning. Prototyping, or making a sandbox to dabble around with something until it works is good. Just make sure to throw it away when done. And hey, being bitten by bad practices is a great motivator for learning the good ones.

Quote:

1.
Some of the guides and tutes I'm looking at are declaring variables and objects right before they need to use them.


Declaring things right where they're needed is good. It keeps the declaration near its usage so that someone reading your code has it fresh in their mind. It keeps the scope of the variable small. If you declare it within a loop, only that loop sees the variable. Less opportunity for overlap or mistakenly using the wrong variable. Finally, keeping the scope small helps ensure that you'll remember to close/delete/cleanup the variable.

And if your methods are as small as they should be, that tends to be the best practice. Though if your methods are long (avoid if possible) and if you need the variables throughout the method (avoid if possible) it's probably best to declare them all together at the top of the method.

Quote:

2.
Is it a good or bad thing to define a struct in a header?


Headers should be the signature of the struct/class unless templated.

Quote:

Is it a good or bad thing to have one header that includes practically everything and his dog, and all other files just include that header?


Bad. If a module includes more than it needs it makes it more difficult to reuse that module.

Quote:

3.
One quite recently made tute uses:
delete [] myApp;

It does this after calling a CleanUp() function to release the D3D objects floating around. I don't think I've used delete [] before. Why has this tutorial done this when the destructor is going to be called automatically anyway? Do you advise using delete [] explicitly? Is it not clearer to instead call the destructor explicitly?


If you new data, delete data. It's not good practice to let the OS clean up after you (and assume that it will). That said, it's increasingly frowned upon to use bald pointers in C++. Smart pointers are your friends and should be preferred where they don't just make more work for you. Though starting out that distinction might be hard to judge.

Quote:

4.
In your WinMain(), what exactly is the point of return 1; ?
And I've seen different examples, so is it:
return 1; or
return 0; or
return wParam.whateverishotatthetime; ?


It's what the OS (or if run from the command line, the shell) sees when your program exits. Generally (at least in the unixy world) 0 is success and not 0 is some variety of not success.

Quote:

5. (Declaring and initialising.)
Is it always advised to intialise (to null or 0 if need be) immediately after declaring a variable, instead of waiting until a time you need to assign something to it?


Absolutely. The #1 cause of errors that only appear when in release mode.

Quote:

6. (Sight between files.)
Correct?


I honestly don't remember.

Quote:

7.
Could someone please simply recommend a good site that clearly explains all things C++. I imagine there's a popular one or two.
(I really want to get into good const practice before I go much further.)


None that I know of.

The faq is alright, but to be honest, C++ sucks far too much for any one site to explain all the nuances, undefined behavior, and odd interactions; let alone clearly.

Share this post


Link to post
Share on other sites
Ah yes that's the site I remember! And thanks for the reminder about smart pointers too. I was using them at one stage but had completely forgotten their existence until you mentioned it. They are now added to the list of old things to apply once more.

My goal is to produce more than to learn, but I'm well aware of the amount of restarts I'm facing. Heck, I'm unhappy with my program already and it's a bloody triangle!

Share this post


Link to post
Share on other sites
"6. (Sight between files.)Correct?"

Sort of. If it's outside of any user-defined scope (the curly braces) it goes into global scope. While you will get name collisions if you try to put multiple variables of the same name into Global scope, there's no actual "visibility" between the multiple cpp files.

The "extern" keyword can (and should) be used for global variables. You declare them extern in the header, put the actual declaration & assignment in a .cpp file and any other files that include the header will now have access to the one global variable (instead of each one having its own variable of the same name, through the wonderful copy-paste of #include).



Share this post


Link to post
Share on other sites
I wouldn't go to random people from the internet for this kind of advice - even my co-oworkers :) Everyone has their own styles and are generally extremely defensive of them. Usually without logic reasons.

Code Complete http://cc2e.com/ has good section on style (with good solid logical reasoning) and is an excellent book all around. I agree with nearly everything :) If your not adverse to slogging through such a hefty book, then I definitely recommend you buy it.

(Also The Pragmatic Programmer: From Journeyman to Master, is also a good book, much thinner and briefly mentions style)

Share this post


Link to post
Share on other sites
Here's a few tips of general programming practice (in order of importance, in my opinion):

#1) Be consistent. Choose a good naming scheme, and stick to it. Having some methods called get_foo(), and others Foo(), and others getFoo() will only cause confusion.

#2) Format your code to be clean, and easy to read. This should go without saying, it's hard to get help if the thought of looking at your code makes people want to puke.

#3) Comment things that aren't completely obvious. A tiny paragraph at the top of a method, is more helpful than commenting what each line in that method does. Some lines might be hard to decipher (esp. when dealing with C++) so it's fine to explain those in a comment too. Don't do this: eat_a_bear(); // Eat a bear, because you'll only clutter your code, but also don't do this farsdnlgkg();, without a comment, I'll never have a clue what that function does ;-).

#4) Know your compiler and linker well. Understand how object files work, what translation units are, etc. If you don't know how the compiler/linker work together, you'll never have a true understanding about "why does it say this is undefined, when it is", etc.

#5) Use common idioms and practices. Pick up all of Scott Meyers C++ books, and read through them. There's no point in reinventing the wheel, and (sadly enough) a lot of people should read it for it's explanation of const-correctness alone.

#6) Learn the differences of pointers, and references. Understand what each is, and take the time to learn how everything is held in memory.

#7) Learn you target platform. There's two different points of view out there, one is the fact that "because C++ isn't O/S specific, you should consider targeting every platform", the other, more reasonable POV is this: If you're going to develop on windows, LEARN exactly how Windows works (same goes for linux/mac, etc). There's too many inconsistencies to be wasting your time trying to get a simple program running on multiple O/S. If you decide at later point to move it to another O/S. Learn how that new O/S works, and porting should be easy. I'd recommend picking up Windows Internals which will REALLY help you out in understanding a lot of what goes under the hood in windows. There's also various books on the Linux kernel, and I'm sure there's plenty for Mac OS as well.

#8) Master your debugger. Period.

#9) Use your brain. This is probably one of the toughest ones to get into people's heads. If you have a problem with some code, the first thing you do shouldn't be posting it on a forum, and getting the answer. You'll frequently forget things that way, and probably won't truly understand what's going on (as in how that fixed it). Take the time to step through your code, figure out exactly what the problem is, and try your hardest to determine what's going on. If you use your debugger, the majority of problems will be easy to figure out. If you still can't seem to get it, read the documentation. Read up on the official documentation regarding the API or SDK's you're using. If you've done your research, and debugging comes up with nothing, start asking around. Someone might not have your answer though (since the first two methods have ruled out most problems, and only really specific/complicated problems should be left to ask about ;-))

I'm sure I have many more, but I'm a bit tired, so I'm going to stop there. Hopefully these help :-D

Share this post


Link to post
Share on other sites
#1
#2
#3
All check.
#4
Not check.
#5
Trying to pick them up, but books are not an option.
#6
Check.
#7
Not check, but agree.
#8
Not check.
#9
Check.

Thanks F1N1TY


I have to mention that I won't be buying any books. Not at this stage in the game anyway. Right now I am 1. Still picking up the hobby again and have not planned how much time I will be committing to it in the future, 2. Just trying to relearn everything I've forgotten, and 3. Travelling.

I don't actually have an address and wouldn't know where to send anything I ordered. Plus, books are a bit of a no no when it comes to pack weight management. But mainly it's that I'm not committing time to this hobby or forcing myself to learn - only following my motivation as my time allows. So please no book recommendations for now.

I know everyone has their own style; I am developing my own as I rip apart every tute I see as well. I am just hoping to get some of the well-known dos and don'ts that always make sense - like the headers question for example.

Two final questions -
In Windows, if WinMain() returns 0 is that also seen as success, like Unix?
And I am wondering if everyone agrees with the practice of not declaring variables until just before they're needed, or if some people can make a case for declaring everything at the top where it's more visible.


Thanks all.

Share this post


Link to post
Share on other sites
I don't think the OS as such cares about the returned value. But someone may be interested, e.g


int result = system("a.exe");
std::cout << "a.exe returned " << result << '\n';

Share this post


Link to post
Share on other sites
Quote:
Original post by Defend
I won't be buying any books

Then read it at a public library. Reading books is essential if you want to get anywhere in C++, because MOST internet tutorials on C++ are total crap.

Quote:
Original post by Defend
I don't actually have an address and wouldn't know where to send anything I ordered.

Ah, so YOU are that guy? :)

Share this post


Link to post
Share on other sites
Quote:
Original post by Defend
6. (Sight between files.)
When I use multiple .cpp files, the IDE is automatically making sure the compiler knows it has to include them all. Okay. And including a header is the same as pasting all the text from that header into the file that is including it. Okay. But can someone please give me some clarification on what "sight" there is between different .cpp files? I'm guessing/assuming that something declared at file scope (outside all the {} braces) is a global and can be seen from anywhere in any of the .cpp files. Correct?

Disregard your IDE: it's a useful tool that insulates you from what's really happening, and you question asks about what's really hapening.

A .cpp file is fed to a program called a compiler, which under Microsoft's Visusal Studio is a program named CL.EXE. The compiler will first preprocess the file by pulling in all #included files and performaing textual replacements, and then parsing and interpreting the C++ code. Finally, it emits another file containing chunks of machine code wrapped in various instructions on how those chunks fit together. These files are called object files and under Microsoft's Visual Studio appear as .OBJ files. Each source file and object file are standalone and know nothing about other source and object files.

The next phase in program construction is to read in all object files and link libraries (which are collections of object files) through a program called a linker, which in Microsoft's Visual Studio is called LINK.EXE. This program reads the labels and instructions wrapping the chunks of machine code in each object file and combines them into a final executable program image. Some object files have a tag saying "I need symbol X", and other have a tag declaiming "I provide symbol X." The linker matches the needs and the provides.

So, you ask in a dazed and exhausted manner, what's with these needs and provides? Isn't that like each .cpp file seeing what's in another? Well, kinda, only it's not the .cpp files, it's the linker after reading the object files.

In C++, names declared at namespace level by default have external linkage. Say what? Well, namespace level is essentially not a member of a class (or struct) and not declared in automatic storage (that is, inside a function). In C, that would be like a "global" variable, only such a beast does not exist in C++ -- the closest equivalent to a global variable in C is a namespace-level variable in the :: namespace in C++. So, to the linker, unless otherwise explcitly specified, any name that appears at namespace level is visible to the linker, so potentially accessible from any .cpp file (assuming it is declared correctly in the code visible in that .cpp file).

Capiche?

Now, if you have two identical names visible to the linker, but each applies to a different object (saym two structs, or an int and a float), you will run into trouble. The ODF (one definition rule) may cause a link failure. Or, linkage may succeed but you will have a difficult to track down runtime error. This is why header files are used: by including identical chunks of source code you up the chances of making sure the names presented to the linker are for identical objects. This is also why doing a full rebuild in Visual Studio can make some bizarre and hard-to-solve runtime problems just up and disappear.
Quote:

So it isn't possible to declare a variable that is only visible to the file it was declared in without actually putting it inside some braces. Right?

Wrong. You can give any namespace level variable internal linkage using the static keyword in its declaration.

Share this post


Link to post
Share on other sites
lol DevFred.

But even the public library isn't a real option. Odds of finding any recommended C++ book in a country that doesn't speak English natively? Plus I'm typically only in one town for a week or so at a time. I'm not quite, but almost that guy.

For now I just avoid seeing anything I read in a tute as "the right way", and only read it as "a way".

Bregma, thank you for yor excellent explanation. I'll go and learn more about static and extern now.

Share this post


Link to post
Share on other sites
The easiest thing for any programming language is to remember the enter key is free to press. It's not like you're trying to fit your code on a sheet of paper like books have to.

Share this post


Link to post
Share on other sites
Quote:
Original post by anemian
The easiest thing for any programming language is to remember the enter key is free to press.

Not in Haskell and Python.

Share this post


Link to post
Share on other sites
As someone else recommended, the book "Code Complete" is a good source of style and practice information, with explanations. I don't agree with everything in there perfectly, but I would be happy to work on a project that followed his recommendations.

I will give my opinions on the below:

Quote:
Original post by Defend
VC++2008, Direct3D9
Alright, I have the damn triangle on the screen now, good for me. But the more I write, the more I remember the hundreds of good things I could be doing... that I have completely forgotten. The back of my mind is vaguely screaming about these wrongs I'm committing while the front is just trying to learn like a person who doesn't know they exist.

So here are some questions on the best ways to go about things. No need to answer all of them... bit of a wordy post this.

1.
Some of the guides and tutes I'm looking at are declaring variables and objects right before they need to use them. For example:
HWND hWindow = CreateWindow(blah blah blah);

That feels a bit spontaneous to me, but of course makes sense when asked 'Why declare something until you know you need it?'. So do people advise declaring objects as much as possible at the top of their scope, or as close as possible to the point where they're needed?


"Code Complete" recommends that you put your variable definitions as close to where you use them as possible. The farther apart they are, the harder to read and understand the code. That makes good sense to me, and in fact I think the main reason that you see things the other way, is that C (not C plus plus) requires variable definitions at the top of a function.

Quote:
2.
Is it a good or bad thing to define a struct in a header?


Good. It is generally accepted as good practice to put all of your definitions (structs, classes, types, function prototypes) in a header file, and the actual code in a separate code file. There are exceptions of course, but I'd advise sticking as close to that as you can.

Quote:
Is it a good or bad thing to have one header that includes practically everything and his dog, and all other files just include that header?


It depends on the project size. In a small project, it might be OK. In a large project, your header file would get to big to easily understand.

I usually try to group definitions by functionality. If you're writing good, object-oriented C plus plus, this usually means each class gets its own header file.

The advantage of breaking things up is that you can more easily find the header definitions you want, and you don't have to search through monstrous files to find what you want. Things may compile faster too if you can limit the number of header files included in each code file.

Quote:
3.
One quite recently made tute uses:
delete [] myApp;

It does this after calling a CleanUp() function to release the D3D objects floating around. I don't think I've used delete [] before. Why has this tutorial done this when the destructor is going to be called automatically anyway? Do you advise using delete [] explicitly? Is it not clearer to instead call the destructor explicitly?


delete() should be used for things that you created via new(). You could also consider using some sort of smart pointer to delete things automatically for you, so you don't forget and leak memory. But it's a good idea to understand new/delete so you understand what the smart pointer is doing.

delete with brackets is specifically for things that you created via new with brackets; that is, an array of things. So, "pFoo = new int[10];" should get a matching "delete[] pFoo;".

Quote:
4.
In your WinMain(), what exactly is the point of return 1; ?
And I've seen different examples, so is it:
return 1; or
return 0; or
return wParam.whateverishotatthetime; ?


Usually, it doesn't matter. If you are doing some sort of multi-process application, that value is the return value of the application. If you are really interested, read up on CreateProcess() (in MSDN) and associated stuff. But you don't need to.

Quote:
5. (Declaring and initialising.)
Is it always advised to intialise (to null or 0 if need be) immediately after declaring a variable, instead of waiting until a time you need to assign something to it?


"Code Complete" says your code will be more readable if you can keep the scope of the variables use as small as possible. So, if possible, declare, initialize, and use it, as close together as possible. I tend to agree.

Quote:
To make it more complicated, I remember there being a difference between initialising on the same line as declaration, and initialising after the declaration is complete. Anyone know what I'm talking about and can point out when this difference matters?


You're probably talking about class initializers (or whatever they are called); how you initialize variables in a classes' constructor. Some people get very religious about them, but I think either is fine. Whatever works for you.

Quote:
6. (Sight between files.)
When I use multiple .cpp files, the IDE is automatically making sure the compiler knows it has to include them all. Okay. And including a header is the same as pasting all the text from that header into the file that is including it. Okay. But can someone please give me some clarification on what "sight" there is between different .cpp files? I'm guessing/assuming that something declared at file scope (outside all the {} braces) is a global and can be seen from anywhere in any of the .cpp files. Correct?


If you want to use a variable in multiple code files, the think to do is to _declare_ it somewhere in a header file. This is done "extern int iFoo;". Then, in _one_ code file, you define the variable "int iFoo;". This causes there to be one (and only one) variable named iFoo, and anyone who includes the header file will be able to "see" that variable.

Quote:
So it isn't possible to declare a variable that is only visible to the file it was declared in without actually putting it inside some braces. Right?


The way to do this is to use the static keyword "static int iFoo;". "static" tells the compiler that that variable is only visible within that specific code file. Otherwise, you might have competing iFoo variables defined in multiple code files, which can cause confusion and should be avoided.

Note that if you are writing object-oriented C plus plus, almost all of your variables will be class members, and you won't have these kinds of issues very often.

Good luck!
Geoff

Share this post


Link to post
Share on other sites

This topic is 3298 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

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

Create an account

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

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this