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.
ReadMe.txt
file (but not any of the automatically
generated files like stdafx.h
) present in the zip file you are going to submit? (2) if you are
working on Linux, are they all listed in the Makefile, did
you run 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 unique non-zero value for each different kind of failure otherwise:
please avoid using the same non-zero return value for different kinds of errors.
With a number of different return values are provided, it's also good practice to
document what the different return values mean through (1) giving the return values descriptively
named labels (see guideline below about not using hard coded numbers); (2) providing comments
in the program source code; and (3) describing the kind of error that occurred (and how to avoid it)
in the usage message(s) and error messages the program generates.
ReadMe.txt
on Windows) file all the error messages your
program can generate, and the conditions under which each one
can arise.
ReadMe.txt
on Windows) file explaining why not.
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.
Problems with use of coding idioms can result in either major deductions (for example, for causing a memory leak by failing to delete memory that was allocated by new) or minor deductions during grading.
exit()
to end the program in the middle of a function or class (or struct)
method. Doing so interferes with the normal graceful shutdown of the
program, and the release of resources the program acquired during
its execution. It's better to use exceptions or return values to
cause the program to unwind its call stack and return an appropriate value (indicating success or failuer) from its
main
function, and it can be especially helpful to use smart pointers
in conjunction with this approach to ensure dynamically allocated
resources are cleaned up within appropriate scopes.
NULL
; use 0
(or with a C++11 adherent compiler
use nullptr
) instead. C++ allows any pointer to be compared to or assigned
0
in old and new compilers alike. The definition of NULL
is
platform dependent in some older compilers, so it may be difficult to use it portably.
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 and smart pointers
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.
If you need to use a cast, you also need to provide a comment explaining why.
= delete
), but then not defined.
new
or
make_shared
. If it's necessary to be able to
destroy class or struct instances from outside the class or struct (or its derived
classes or structs), then provide a member function that destroys instances
of the class or struct using delete
, but be careful to
make sure there are no remaining aliases to the instance at that point.
BOOL
, or similar compiler-specific types, even if a
compiler supports them. For portability, always 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 conditional ?:
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.
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
), or use RAII and other
programming idioms to make code exception-safe.
.cpp
) files, placing
declarations in header (.h
) files, and using the
#include
directive to make declarations visible where
needed. Note that some compilers assume 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 or struct templates and function templates
from definitions for non-template classes or structs and non-template functions by placing
them into separate source (.cpp
) or (depending on the compiler)
header (.h
) 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 or struct and function declarations in foo.h go here... #endif /* FOO_H */
Files that contain type-parameterized classes or structs should follow this style:
#ifndef FOO_T_H #define FOO_T_H // Class or struct template and function template declarations for foo_t.h go here... #if defined (TEMPLATES_REQUIRE_SOURCE) #include "Foo_T.cpp" #endif /* TEMPLATES_REQUIRE_SOURCE */ #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...
This ensures 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 need to be included in the corresponding header file).
int main (int, char *[]) { // Rest of the definition of main ... return 0; }
make_shared
and shared_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 modern team programming environments.
For example, 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 enum label (or if absolutely
necessary a precompiler constant) like one of the following:
const int expected_argument_count = 3; enum command_line_arguments {program_name, input_file, output_file}; #define SUCCESS 0