Problems with program compilation and execution will often result in major (5-10 point) deductions during grading, though for less severe concerns (for example a compiler warning that should have been fixed but does not indicate a deeper problem with the program, or forgetting to add a usage message for a program) minor (1-3 point) deductions are possible.
make test_turnin, and if so did all the files
end up in the TEST_TURNIN directory when you did that?
-Wall switch) to see all
possible compiler warnings about your code, and then fix all those compiler warnings before
submitting your program for grading. If a warning cannot be
fixed, and does not indicate a problem in your code, you may
use a compiler pragma to silence that specific warning, but
then please document that you have done so, and why it was
necessary, both in your readme file and in your code.
main function must return 0 on
successful termination, and a non-zero value otherwise. If a
number of different return values are provided, it's good to
document what the different return values mean both in the program
source code itself and in the usage message the program generates.
One very helpful question to ask throughout the debugging process is "what was the last point where my program seemed to be working correctly, and what was the first point where it seemed to be working incorrectly?" A debugger is in invaluable tool in partially automating this process.
--help command line argument, are
provided to the program. In addition to showing the correct
format for command line arguments, the usage message should
briefly explain what the program does.
Problems with use of coding idioms can result in either major deductions (for example, causing a memory leak by failing to call delete on memory allocated by new -- even better would be to use safe memory management idioms to avoid this problem) or minor deductions (for example, unnecessarily zeroing out a pointer before calling delete on it) during grading.
new and
delete instead of
malloc and
free to allocate and deallocate
memory, respectively.
new to allocate
memory in a program, make sure to find and document where in the
program that memory will be freed with a corresponding
delete. The best way to do this
is to use the safe memory management idioms we'll talk about
as the course evolves. A more general way to state this point
is that you should always know (and comments in your code
should explain where appropriate) the lifetime of any dynamically
allocated piece of memory.
free to deallocate memory
that was allocated with new.
Similarly, never use delete to
deallocate memory that was allocated with
malloc.
ssize_t n_bytes;
if ((n_bytes = foo.send ((char *) &reply_port,
sizeof reply_port)) == -1) // ...
Write it like this:
ssize_t n_bytes =
foo.send ((char *) &reply_port, sizeof reply_port)
if (n_bytes == -1) // ...
if (test)
{
// true branch
}
else
{
// false branch
}
is preferred over:
if (! test)
{
// false test branch
}
else
{
// true test branch
}
return condition;
instead of
if (condition)
{
return true;
}
else
{
return false;
}
or
return condition ? true : false;
(int) foo. Use standard C++ casts, e.g.,
static_cast<int> (foo), instead.
new. If it's necessary to be able to
destroy class instances from outside the class (or its derived
classes), then provide a member function that destroys instances
of the class using delete.
BOOL, or similar types, even if a
compiler supports them. Use the standard C++ bool
type for Boolean variables, instead.
size_t i = 0;
for (size_t j = 0; file_name [j] != '\0'; ++i, ++j)
{
if (file_name [j] == '\\' && file_name [j + 1] == '\\')
++j;
file_name [i] = file_name [j];
}
// Terminate this C-style string.
file_name [i] = '\0';
if (...) else
.... instead of the contional ?:
operator. Your code will be a lot more readable, and this
can help you avoid subtle bugs due to the precedence of
?: compared with other operators
in an expression.
operator==, it must also provide
operator!=. Also, both these operators must be
const and return bool.
for or do or while loop,
or an if
or else statement branch, even if it's only a single
statement. For example:
for (int j = 0; j < argc; ++j)
{
cout << argv [j] << endl;
}
int k = 0;
do
{
cout << argv [k++] << endl;
} while (k < argc);
while (k > 0)
{
cout << argv [--k] << endl;
}
if (0 <= k && k < argc)
{
cout << argv [k] << endl;
}
else
{
cout << "argument position " << k << "is out of range." << endl;
}
new (nothrow))
new)
.cpp) files, placing
declarations in header (.h) files, and using the
#include directive to make declarations visible where
needed. Note that some compilers use a different source file
extension (e.g., .cc or .cxx) but for
this course we will always use the .cpp extension for
our source files.
.h) files. Also
distinguish definitions for class templates and function templates
from definitions for non-template classes and functions by placing
them into separate source (.cpp) files. This will
give you better decomposition of your programs overall, and some
compilers get confused when template and non-template code is
mixed in the same file.
#ifndef FOO_H
#define FOO_H
// Class and function declarations go here...
#endif /* FOO_H */
Files that contain type-parameterized classes should follow this style:
#ifndef FOO_T_H
#define FOO_T_H
// Class template and function template declarations go here...
#if defined (TEMPLATES_REQUIRE_SOURCE)
#include "Foo_T.cpp"
#endif /* TEMPLATES_REQUIRE_SOURCE */
#endif /* FOO_T_H */
Notice that some compilers need to see the code of the
template, in which case the .cpp file must be
included from the header file. In that case to avoid multiple
inclusions of the template .cpp file it should also
be protected as in:
#ifndef FOO_T_CC
#define FOO_T_CC
#include "Foo_T.h"
// Class template and function template definitions go here...
#endif /* FOO_T_H */
.cpp source code file always include the
corresponding header file as the first inclusion (after any environment-specific
mandatory inclusions like stdafx.h on Windows with precompiled headers), like this:
// This is Foo.cpp
#include "stdafx.h" // would not have this on Linux
#include "Foo.h"
// Any other includes go here...
// Definitions go here...
In this way we are sure that the header file is self-contained and can be safely included from some place else (if it seems like other includes should go first, then they probably belong in the corresponding header file).
int main (int, char *[])
{
// Rest of the definition of main ...
return 0;
}
auto_ptr) whenever
possible, in preference to using new and
delete directly.
Problems with program style and documentation usually result in only minor deductions during grading, but even minor deductions can add up quickly if there are a lot of them. In the longer term, practicing a consistent and high quality programming style will make your code more accessible to others, which is essential in team programming environments.
for,
if,
switch, and
while statements should have a
space after the respective control keyword, as in:
for (unsigned int i = 0; i < count; ++i)
++total;
7: instead
use an appropriately named constant variable (or if absolutely
necessary a precompiler constant) like
static const int days_in_a_week = 7;
Error") in a source
or header file name, to make your code files easier to work with on different
platforms and in different development environments. For example,
GNU Make's error messages start with
"Error", and so it's much easier to search for errors if
filenames don't contain "Error".