The following is my approach for a program that simulates a bash terminal.
I have the following:
class Shell
{
    public:
        string currentDir;
        vector<string> cmdHistory;
        vector<pid_t> pidHistory;
    public:
    Shell(string dir)
    {
        this->currentDir = dir;
        this->cmdHistory.clear();
        this->pidHistory.clear();
    };
    // Get the current directory
    string getDir()
    {
        return this->currentDir;
    }
    void setDir(string dir)
    {
        this->currentDir = dir;
    }
    void appendDir(string dir)
    {
        this->currentDir.append(dir);
    }
    void recordCmd (string record)
    {
        cmdHistory.push_back(record);
    }
    void recordPID(pid_t record)
    {
        this->pidHistory.push_back(record);
    }
    void printCmdHistory()
    {
        int size = cmdHistory.size();
        if (size == 0)
            return;
        for (int i = 0; i < size; i++)
        {
            cout << cmdHistory[i] << "\n";
        }
    }
    void clearHistory()
    {
        cmdHistory.clear();
    }
    void clearPIDHistory()
    {
        pidHistory.clear();
    }
    int getPIDsize()
    {
        return this->pidHistory.size();
    }
    pid_t getCurrentPID()
    {
        return this->pidHistory.at(pidHistory.size() - 1);
    }
    pid_t getAtPID(int i)
    {
        return pidHistory.at(i);
    }
};
Which is my object type Shell. I then continue to call a new type of Shell object in main in an infinite loop and do the following:
    Shell newShell(currentDir);
    do
    {
        cout << "#";
        getline(cin, inputBuffer);
        newShell = processLine(newShell, inputBuffer);
        cout << endl;
        
    }while(inputBuffer.compare("byebye") != 0);
Similarly, I am doing the same inside the function processLine():
// #background:
        else if (args[0].compare("background") == 0)
        {
            if (args.size() > 1)
            {
                // CASE I: Full path
                if (args[1][0] == '/')
                {
                    // There's a parameter
                    if (args.size() > 2)
                    {
                        currentShell = backgroundProgramHelper(currentShell, args, true);
                    }
                    else
                    {
                        currentShell = backgroundProgramHelper(currentShell, args, false);
                    }
                }
                // CASE II: Relative path
                else
                {
                    // Append program to current directory address
                    args[1] = currentShell.getDir() + "/" + (args[1]);
                    // There's a parameter
                    if (args.size() > 2)
                    {
                        currentShell = backgroundProgramHelper(currentShell, args, true);
                    }
                    else
                    {
                        currentShell = backgroundProgramHelper(currentShell, args, false);
                    }
                }
            }
The function backgroundProgramHelper() does the following:
Shell backgroundProgramHelper(Shell currentShell, vector<string> args, bool flag)
{
    if (flag)
    {
        // Get rid of '-'
        string flag = args[2];
        string delimeter = "-";
        size_t pos = flag.find(delimeter);
        string token = flag.substr(0, pos);
        args[2] = flag.erase(0, pos + delimeter.length());
        // DMA a char * for the amount of parameters.
        // I made the assumption based on the example given
        // that the total amount of parameters passed would 2
        char * arguments[4];
        arguments[0] = (char*)args[1].c_str(); // program
        arguments[1] = (char*)args[2].c_str(); // flag
        arguments[2] = (char*)args[3].c_str(); // flag
        arguments[3] = NULL;
        pid_t pid = fork();
        
        // ERROR
        if (pid == -1)
            perror("ERROR: Failed to fork");
        // Child
        if (pid == 0)
        {
            cout << "child: " << pid << endl;
            if (execvp (arguments[0], arguments) == -1)
            {
                perror("exec");
            }
        }
        // Parent
        if (pid > 0)
        {
            wait(0);
            currentShell.recordPID(pid);
            cout << "\nparent: " << pid << endl;
        }
    }
    else
    {
        char * arguments[2];
        arguments[0] = (char*)args[1].c_str(); // program
        arguments[1] = NULL;
        pid_t pid = fork();
        
        // ERROR
        if (pid == -1)
            perror("ERROR: Failed to fork");
        // Child
        if (pid == 0)
        {
            cout << "child: " << pid << endl;
            if (execvp (arguments[0], arguments) == -1)
            {
                perror("exec");
            }
        }
        // Parent
        if (pid > 0)
        {
            wait(0);
            currentShell.recordPID(pid);
            cout << "\nparent: " << pid << endl;
        }
    }
    return currentShell;
}
It takes the currentSHell and then updates the history of PID with any program that was run. The problem is if I make backgroundProgramHelper type void and not return a Shell type variable to update the one I passed, then my object in main doesn't get updated with the PID history of the programs run. I think this is a fundamental problem in passing by values and passing by reference, but again, I am not sure why is it that I have to do currentSHell = backgroundProgramHelper(Shell currentShell, vector args, bool flag), in order for my PID history to get updated.
