CS 70

Why Multiple Files?

  • Duck speaking

    We always seem to make our executables in two steps. First compiling, and then linking. It seems like a pain, and I figured out I can actually it do it with just one line. Why are we making it complicated?

  • LHS Cow speaking

    We'll see that it better reflects what we do when we split programs up into multiple files.

  • Duck speaking

    Why split things up into multiple files?

  • LHS Cow speaking

    Let's see why!

An Example

Here is cowchat.cpp, which implements a surprisingly realistic cow chatbot:

#include <iostream> // for std::cin, std::cout
#include <string>   // for std::string

// Prints the given number of "moos"
void printMoos(int numMoos) {
    std::cout << "Moo";
    for (int i = 1; i < numMoos; ++i) {
        std::cout << " moo";
    }
    std::cout << ".";
}

int main() {
    std::cout << "Chat with a cow!";
    std::string userIn;
    do {
        std::cout << std::endl  // New line
                  << "> ";      // The prompt
        std::getline(std::cin, userIn);
        int nMoos = 1 + userIn.size() % 2;
        //  ^-- Either 1 or 2 moos
        printMoos(nMoos);
    } while (std::cin.good() && userIn != "");
    std::cout << "\n\nBye!\n";

    return 0;
}

In the homework we usually ask you to turn on a bunch of useful compiler options. But for right now, let's strip the command line down to the bare minimum.

In order to compile cowchat.cpp into an object file, cowchat.o, we would run the command

clang++ -c cowchat.cpp

The -c option tells the compiler to take the given source file and produce an object file.

In order to then produce an executable file, we would run the command

clang++ -o cowchat cowchat.o

The -o cowchat option tells the compiler to name the output file cowchat. Since we didn't give the -c option, the compiler will produce an executable.

  • Horse speaking

    What would happen if we didn't say -o cowchat?

  • LHS Cow speaking

    As we mentioned in a previous lesson, the compiler would give the output file a default name: a.out

Here is cowmath.cpp, which adds two numbers, cow-style.

#include <iostream>

// Prints the given number of "moos"
void printMoos(int numMoos) {
    std::cout << "Moo";
    for (int i = 1; i < numMoos; ++i) {
        std::cout << " moo";
    }
    std::cout << ".";
}

int main() {
    std::cout << "Cow math!\n";

    std::cout << "Enter the first number: ";
    int x = 0;
    std::cin >> x;

    std::cout << "Enter the second number: ";
    int y = 0;
    std::cin >> y;

    if (std::cin.good()) {
        std::cout << "The cow goes: ";
        printMoos(x + y);
        std::cout << std::endl;
    } else {
        std::cout << "\n\nSomething went wrong!\n";
    }

    return 0;
}

Give the commands to take cowmath.cpp and produce an executable named cowmath. (Provide the minimal command without any optional flags.)

The minimal commands to get the cowmath executable are

  • clang++ -c cowmath.cpp
  • clang++ -o cowmath cowmath.o

The Problem

Each of these programs might be okay by itself, but seen together as a pair of programs, they provide an example of a coding practice that is best avoided.

Can you identify the issue?

The Problem: Code Duplication

The problem is code duplication! The same function appears in both files.

It would be much better to just write this function once.

  • Duck speaking

    Copying and pasting a function from one file to another is quick and easy. Why is it so problematic?

  • Cat speaking

    It's because it makes code less maintainable. If you have to change something, you have to change it everywhere the same code shows up!

  • LHS Cow speaking

    That's right. So what are we going to do about this?

  • Duck speaking

    Can we put that function in its own file and then share it with both programs?

  • LHS Cow speaking

    That's exactly what we're going to do.

(When logged in, completion status appears here.)