Looking for particular tips on good programming practice

Started by
13 comments, last by gdunbar 15 years, 4 months ago
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.

Stephen M. Webb
Professional Free Software Developer

Advertisement
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.
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.
The only valid measurement of code quality: WTFs/minute
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.
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

This topic is closed to new replies.

Advertisement