Search code examples
c++arraysclasstemplatesdefinition

How do I fix blank output from trying to display an array of string objects through an overloaded and templated insertion operator?


I've declared container<std::string> c1; in main() to store string objects of 10 elements i.e. aa bb cc dd . . . jj; but, when I compile the code, the output is blank. If I change the type in those angular brackets to char and use one letter, it works fine. I'm thinking that there's something wrong with what's being passed in the parameters for the overloaded insertion operator. I've tried adding const and making one of the parameters container<T2> const &cobj--it didn't help solve the issue.

#include <iostream>
#include <string>

template<typename T>
class container
{
    template <typename T2>
    friend std::ostream& operator<<(std::ostream& out, container<T2> &cobj);
    // Postcondition: display the contents of container object cobj in the format shown in the below sample outputs
public:
    container();
    // Postcondition: data member n is initialized to -1 and all elements in the empty arr array are initialized to zero
    bool isEmpty();
    // Postcondition: returns true if nothing is stored in the container; returns false otherwise
    bool isFull();
    // Postcondition: returns true if the container object (i.e., the arr array) is full; returns false otherwise
    int size() const;
    // Postcondition: returns the “size” which is the actual number of elements currently stored in the containe robject; size <= capacity
    int capacity();
    // Postcondition: returns the storage capacity of the container. 
    bool insertBack(const T& val);
    //  Precondition: the container object is not full
    // Postcondition: if arr array is not full, n is incremented by 1; returns true with val is inserted at the end of the arr array 
    //                 Otherwise, returns false; the value is not inserted and program execution continues.

private:
    static const int CAPACITY = 10;     // physical size of the arr array or the storage capacity of a container object
    T arr[CAPACITY];            // arr array can store up to CAPACITY  (10 in our case) elements of any type 
    int n;                      // n is used as the subscript for the arr array. n is initialized to -1 for an empty array
                                // Each time a new value is inserted into the arr array, n must first be incremented 
                                // by 1. Since n has been initialized to -1, the first inserted value is stored in arr[0],
                                // and the 2nd inserted value will be in arr[1], etc.  and the nth inserted value will be 
                                // stored in arr[n – 1]. Obviously, n + 1 represents the actual number of elements
                                // stored in the array after n rounds of insertion.         
};

template<typename T2>
std::ostream& operator<<(std::ostream& out, container<T2> &cobj)
{
    std::cout << "Container storage capacity = " << cobj.capacity() << std::endl
        << "Currently, container contains " << cobj.size() << " elements." << std::endl
        << "The contents of the container:" << std::endl;

    static int funcCalls = 0; // A variable that collects the calls made to this function.
                              // The first call is needed to later output an unique string.
    funcCalls++;

    if (cobj.isEmpty() && funcCalls == 1)
    {
        std::cout << "*** Container is currently empty!" << std::endl << std::endl
            << "The container object is empty!" << std::endl
            << "Currently, the container object contains " << cobj.size() << " element(s) or value(s)";
    }
    else if (cobj.isEmpty())
    {
        std::cout << "*** Container is currently empty!";
    }
    else
    {
        for (int i = 0; i < cobj.size(); ++i)
        {
            std::cout << cobj.arr[i] << ' ';
        }
    }

    std::cout << std::endl << std::endl;

    return out;
}

template<typename T>
container<T>::container()
{
    n = 0;
    arr[CAPACITY] = {0};
}

template<typename T>
bool container<T>::isEmpty()
{
    return n == 0;
}

template<typename T>
bool container<T>::isFull()
{
    return n == CAPACITY - 1;
}

template<typename T>
int container<T>::capacity()
{
    return CAPACITY;
}

template<typename T>
int container<T>::size() const
{
    return n;
}

template<typename T>
bool container<T>::insertBack(const T& val)
{
    if (!isFull())
    {
        n++;
        arr[n - 1] = val;
        return 1;
    }
    else
    {
        return 0;
    }
}

int main()
{
    container<std::string> c1;

    std::cout << "We now insert 10 values at the back of the array one at a time:" << std::endl;    

    c1.insertBack("aa");
    c1.insertBack("bb");
    c1.insertBack("cc");
    c1.insertBack("dd");
    c1.insertBack("ee");
    c1.insertBack("ff");
    c1.insertBack("gg");
    c1.insertBack("hh");
    c1.insertBack("ii");
    c1.insertBack("jj");
    std::cout << c1;

}

Solution

  • In the default constructor

    template<typename T>
    container<T>::container()
    {
        n = 0;
        arr[CAPACITY] = {0};
    }
    

    you are trying to initialize a non-existent element of the array with the index CAPACITY. As a result your program has undefined behavior,

    In fact there is no need to initialize the array. It will be default-initialized.

    template<typename T>
    container<T>::container() : n( 0 )
    {
    }
    

    All these functions, isEmpry, isFull, capacity and size should be declared with the qualifier const.

    The function isFull has a logical error. The container will be full when its size is equal to its capacity

    template<typename T>
    bool container<T>::isFull() const 
    {
        return n == CAPACITY;
    }
    

    The friend function could be a non-template function defined within the class definition like

    template<typename T>
    class container
    {
        friend std::ostream& operator<<(std::ostream& out, const container<T> &cobj)
        {
            //...
        }
        //...
    

    If you will make all these changes then the program output will be

    We now insert 10 values at the back of the array one at a time:
    Container storage capacity = 10
    Currently, container contains 10 elements.
    The contents of the container:
    aa bb cc dd ee ff gg hh ii jj