In this class, we wish to instill coding practices that will help you keep your code organized and easy to read, so that when you work with code in a professional environment your teammates don't curse your name as they try to integrate your work with their own. Having good coding style also helps your TAs interpret your code in office hours and when grading!
To these ends, we ask that you follow these guidelines when you program assignments in this course.
// Good indentation: void f() { for (int i = 0; i < 10; i++) { std::cout << i << std::endl; } } // Bad indentation: void f() { for(int i = 0; i < 10; i++) { std::cout << i << std::endl; } }
void preSpace() { // This is good! } void preNewLine() { // This is also good! } void badFormat(){ // Bad! There is nothing between the closing parenthesis and opening curly brace! }
Person::Person() : name("Bob"), age(50) {} // Good! Person::Person() : name("Bob"), age(50) // Good! {} Person::Person() : name("Bob"), age(50) { // Bad! Wastes space! }
So, keep this in mind the next time you feel tempted to use a braceless conditional.if (condition) goto fail; goto fail; // This line would always be called!
if (condition) { doAThing(); // Good! We've safely encapsulated the results of the conditional in their own scope. } if (condition) doAThing(); // Bad! It's valid C++ code but leaves you open to human error! if (condition) doAThing(); doAnotherThing(); // Even worse! The indenting implies doAnotherThing // will only be called when condition is true, but // it will ALWAYS be called!
Person* p = new Person("Bob", 40, "Kansas City", "6ft, 0in");
// The code below will only be read by the compiler if // #define LOOKATME is put in the code somewhere before the #ifdef #ifdef LOOKATME int x = 0; x = x / 0.f; std::cout << x << std::endl; #endif
// Here is an example of some nicely formatted commenting // This function returns the color of an image at the // specified coordinates. Color3f PixelColorAt(Image* i, Point2f p) { // ... } // Here is an example of poorly formatted commenting Color3f PixelColorAt(Image* i, Point2f p) {//This function returns a color. What is the color? Listen, pal, I just work here. How am I supposed to know how every little function works?? // ... }
int var; // Good int thisIsAVariable; // Good int this_is_a_variable; // Good int ThisIsAVariable; // Bad! Should NOT begin with a capital letter.
const float TWO_PI = 6.28318530718f;
int functionName(); int FunctionName(); int function_name(); int Function_Name();
// Good class declaration: // A 3D point where the x, y, and z coordinates are floats class Point3f { // ... }; // Bad class declaration: // Lowercase first letter and underscores make this // look like a variable name, not a class name class point_3_f { // ... };
enum Color { RED, GREEN, BLUE, LIGHT_RED };
float newPosition = oldPosition + velocity * time; // Good variable names! You can immediately tell what's happening here. float x2 = x1 + dx * t; // Still okay, but the meaning is a bit opaque... float a = aa + aaa * aaaa; // This looks like an exaggeration of bad coding style, // but we have seen variables named like this! Don't do it!
x + y // Good x+y // Bad
int numbers[10]; // Good int numbers [10]; // Bad numbers[0] = 5; // Good numbers [0] = 5; // Bad
a + (b + c) // Good a +(b + c) // Bad a + ( b + c ) // Bad
if (x > 5) // Good if(x > 5) // Bad if( x > 5 ) // Bad
print("Hello, world"); // Good print ("Hello, world"); // Bad print( "Hello, world" ); // Bad
int* pointer1; // Good int *pointer2; // Good int * pointer3; // Bad! Looks like a multiplication! int& ref1; // Good int &ref2; // Good int & ref3; // Bad!
int* x = new int(5); *x = 10;
int *x, *y; // Looks consistent and is obviously two int pointers int* x, *y; // Looks inconsistent and at a glance might look like x is a regular int, // or that y is an int**
class Person { private: int age; public: // getAge() does not modify any members of the Person class, // so it has been declared const. This tells the compiler that // this function is guaranteed to not modify the class instance // that invokes it. int getAge() const { return age; } void sayHello() { std::cout << "hello" << std::endl; } }; const Person b; b.getAge(); // No problem, since getAge() is const. b.sayHello(); // Compiler error! Even though the function itself // does not modify the invoking Person, without the // const declaration the compiler does not know this // and will throw an error.
void lookAtMemory1(Person const * const readOnlyPtr) { int age = readOnlyPtr->age; std::cout << age << std::endl; } void lookAtMemory2(const std::unique_ptr<Person>& readOnlyPtr) { int age = readOnlyPtr->age; std::cout << age << std::endl; } int main() { std::unique_ptr<Person> p = std::make_unique<Person>; lookAtMemory1(p.get()); lookAtMemory2(p); }
void incrementInt(int* x) { *x = *x + 1; } void main() { int y = 5; incrementInt(&y); // Now y is 6. }
template <class T> using uPtr = std::unique_ptr<T>; template <class T> using sPtr = std::shared_ptr<T>; #define mkU std::make_unique #define mkS std::make_shared void main() { uPtr<int> myInt = mkU<int>(5); sPtr<float> myFloat = mkS<float>(3.5); }
class Person { public: int age; std::string name; Person() : age(-1), name("None") {} Person(int a, std::string n) : age(a), name(n) {} }; // Constructs a vector containing ten People, each age 55 and with the name "Bob" std::vectorpeople = std::vector (10, Person(55, "Bob"));
std::vectorarr {5, 10, 15, 20}; for (float f : arr) { std::cout << f << std::endl; }
std::vectorpeople(100, Person(50, "Bob")); for (Person& p : people) { p.name = "Bobbert"; }
class Person { private: std::string m_name; int m_age; Town* mp_homeTown; public: // The list that follows the colon is the initialization list // Each member of the class is constructed with the input variable // from the function inputs. You could also invoke functions within // each item of the initialization list to instantiate each member. Person(std::string name, int age, Town* t) : m_name(name), m_age(age), mp_homeTown(t) {} }
int* num = nullptr;
// Functional loop, but n will stay in memory after the loop is done... // Also, the loop body contains the code that updates our iterator variable // rather than making that part of the loop. Node* n = linkedList.start; while (n != nullptr) { std::cout << n->value << std::endl; n = n->next; } // The iterator variable (n) and the iterator update (n = n->next) // are nicely contained in the loop header, and the loop body is // reserved for operations not directly related to iterating over // each element of the list for (Node* n = linkedList.start; n != nullptr; n = n->next) { std::cout << n->value << std::endl; }
int x; x = 5; // Bad! Why waste space by assigning x a value on a different line? int* y = nullptr; // Good! Declaration and initialization are on the same line, and we've made our pointer null for now.
// Good! int Person::getAge() const { return this->age; } // Bad! Don't need to clutter the stack with a temporary variable // that will be removed from memory on the next line! Would be even // worse if the thing we were returning were a "large" data type! int Person::getAge() const { int myAge = this->age; return myAge; }
// Too long! QString filename = QFileDialog::getOpenFileName(0, QString("Load Scene File"), QDir::currentPath().append(QString("../..")), QString("*.json")); // Much nicer to read QString filename = QFileDialog::getOpenFileName(0, QString("Load Scene File"), QDir::currentPath().append(QString("../..")), QString("*.json"));
void modifyTheInt(int* i) { *i = 10; // i is dereferenced to set the int's value to 10. } int x = 0; modifyTheInt(&x); // x's address is passed into the function
// Even if the class ObjectThatTakesUpLotsOfMemory uses a whole megabyte of stack space, when func() is invoked, // only a pointer's worth of memory is added to the stack (8 bytes on most modern systems). // Additionally, o is "read-only" because it has been declared as being const. void func(const ObjectThatTakesUpLotsOfMemory& o) { // ... } ObjectThatTakesUpLotsOfMemory object; func(object);
class Person { public: std::vector<Person*> children; // A complex member variable // Rest of class here }; Person p = Person(); const std::vector<Person*>& c = p.children; // Avoid copying p.children by using a reference. Ensure we don't modify it because c is a const ref. // Read data from children here