-1

I am trying to run this program on provided google test cases but all the test cases fail.

  • Function String operator+(const char& str) const appends a char at the end of the String.
  • Function String operator+(const String& str) const appends a String at the end of the String
  • Function String operator+(char*& str) const appends a String at the end of the String.

Where is the problem?

#include<iostream>
#include<string>
#include <cstdlib>

using namespace std;

class String
{
private:
    char* s;
    int size;
public:
    String()
    {
        s = NULL;
        size = 0;

    }
    String(const char* str)
    {
        size = strlen(str);
        s = new char[size];
        for (int i = 0; i < size; i++)
            s[i] = str[i];
    }
    String(const String& obj)
    {
        size = obj.size;
        s = new char[size];
        for (int i = 0; i < size; i++)
            s[i] = obj[i];
    }
    String(int x)
    {
        size = x;
        s = new char[size];
    }
    char& operator[](int i)
    {
        return *(s + i);
    }
    const char operator[](int i) const
    {
        return *(s + i);
    }
    String operator+(const char& str) const
    {
        String s1;
        s1.s += str;
        return s1;
    }
    operator int() const
    {
        int m;
        m = size;
        return m;
    }
    String operator+(const String& str) const
    {
        String iSt = "";
        int length = 0;
        length = strlen(s);
        length += strlen(str.s);
        iSt.s = new char[length];

        strcpy(iSt.s, s);
        strcat(iSt.s, str.s);

        return iSt;
    }
    String operator+(char*& str) const
    {
String iSt = "";
    int length = 0;
    length = strlen(s);
    length += strlen(str);

    iSt.s = new char[length];

    strcpy(iSt.s,s);
    strcat(iSt.s,str);

    return iSt;
    }
};
TEST(String, ArithmeticOperatorsplus) {
    String s1("abcd");

    String s2;
    s2 = s1 + 'e';
    ASSERT_EQ('d', s2[3]);
    ASSERT_EQ('e', s2[4]);
    char* c = (char*)"asdfgh";
    s2 = s1 + c;
    ASSERT_EQ('d', s2[3]);
    ASSERT_EQ('a', s2[4]);
    ASSERT_EQ('g', s2[8]);
    ASSERT_EQ(10, (int)s2);
    String s3 = s1 + s2;
    ASSERT_EQ(14, (int)s3);
    ASSERT_EQ('s', s3[9]);

}
James Z
  • 12,209
  • 10
  • 24
  • 44
  • Have you tried running your code line by line in a debugger while monitoring the values of all variables, in order to determine at which point your program stops behaving as intended? If you did not try this, then you may want to read this: [What is a debugger and how can it help me diagnose problems?](https://stackoverflow.com/q/25385173/12149471) You may also want to read this: [How to debug small programs?](https://ericlippert.com/2014/03/05/how-to-debug-small-programs/) – Andreas Wenzel Apr 22 '22 at 21:33
  • Questions seeking debugging help should generally provide a [mre] of the problem, which includes a function `main`. – Andreas Wenzel Apr 22 '22 at 21:37
  • Recommendation: Add `#include` – user4581301 Apr 22 '22 at 21:42
  • I have used the #include but still the problem is there in the code – Mudassir Waheed Apr 22 '22 at 21:45
  • Consider resolving the errors and warnings highlighted here: https://godbolt.org/z/KKrzoqM4q – user4581301 Apr 22 '22 at 21:47
  • Looks like you're not adding a null terminator at the end of your strings in the constructors. – JarMan Apr 22 '22 at 21:48
  • Hint: [What is The Rule of Three?](https://stackoverflow.com/questions/4172722/what-is-the-rule-of-three) – user4581301 Apr 22 '22 at 21:49
  • I have seen this errors and warnings but i am unable to overcome. Can you please help to make this code run perfectly? – Mudassir Waheed Apr 22 '22 at 21:50
  • In `operator+`, rethink `s1.s += str;`. This is pointer arithmetic changing the address `s` stores. It does not append to the string at `s`. This function needs to be a lot more complicated, I'm afraid. `S1` needs to be a copy of `this`, but with one extra space to hold `str`. – user4581301 Apr 22 '22 at 21:54
  • What could be the possible way to write this function? – Mudassir Waheed Apr 22 '22 at 21:55
  • Get a big enough empty string: `String s1(size +1);`. Copy the current string's data into the new string: `memcpy (s1.s, s, size);`. Append `str`: `s1.s[size] = str;`. Return the new string: `return s1;`. This will solve the first of MANY bugs in the code. Recommendation: Never write this much code all at once. Write a few lines, one function at the most, and just enough to do one easily testable thing. Test the code to ensure that one thing is done correctly for every input you can think of. Once you know those few lines work, write a few more, test, repeat. You never want more than one bug. – user4581301 Apr 22 '22 at 22:04
  • Regarding @JarMan 's comment, about terminating the strings, I'm not sure you need to do this, but if you decide not to null-terminate, know that `strlen`, `strcpy`, and all of the other string manipulation functions in cstring utterly depend on that terminating null and cannot be used. – user4581301 Apr 22 '22 at 22:08

1 Answers1

0

The biggest problem here is that you are basically still on C and not on C++.

You are using C-functions like strlen and `strcat which even do not compile with my C++ compiler.

Then, you forget the terminating '\0' character, if you copy from C-string literals. And then, neither strlen nor strcat can work correctly.

You are calling '+=' but have no operator defined. This will not work.

You did not define assignment operators at all. The compiler generated assignments will copy the pointer and not do a deep copy.

You are doing a lot of new but never release the memory with delete.

I get 25 compiler warnings wit "-wall".

So, you need to refactor a lot.

I once create a similar dynamic array (string), which is far from perfect, but it may give you an idea, how to implement your own String . . .

#include <iostream>
#include <sstream>
#include <initializer_list>

// -----------------------------------------------------------------------------------------------
// Definition of simple dynamic array class
template <typename T>
class DynamicArray {
    // The Dynamic Array has an initial capacity. 
    // If more elements will be added, there will be a reallocation with double capacity
    static constexpr unsigned int InitialCapacity{ 8 };

    // Internal data ------------------------------------------------------------------------------
    T* data{};                                 // Dynamic Storage for Data
    unsigned int numberOfElements{};           // Number of elements currently in the container
    unsigned int capacity{ InitialCapacity };  // Current maximum capacity of the container
public:
    // Construction and Destruction ---------------------------------------------------------------
    DynamicArray();                            // Default constructor. Allocate new memory
    DynamicArray(const unsigned int size);     // Constructor for a given size. Allocate new memory
    DynamicArray(const DynamicArray& other);   // Copy constructor. Make a deep copy
    DynamicArray(DynamicArray&& other);        // Move constructor

    // Special constructors
    template <class Iterator> DynamicArray(Iterator begin, Iterator end);   // Initialize from range   
    template <int N> DynamicArray(const T(&other)[N]);                      // Initialize from C_Sytle array,e.g. a string literal
    template <int N> DynamicArray(T(&other)[N]);
    DynamicArray(const std::initializer_list<T>& list);                     // Take data from initializer list

    ~DynamicArray();                            // Destructor: Release previously allocated memory

    // Housekeeping ---------------------------------------------------------------
    bool empty() const;                         // Do we have elements in the container? Do not mix up with capacity
    void clear();                               // Clear will not delete anything. Just set element count to 0
    unsigned int size() const;                  // How many elements are in the container

    // Main working functions
    void push_back(const T& d);                 // Add a new element at the end

    // Operators for class------------------------ ---------------------------------------------------------------

    T operator[] (const unsigned int i) const;  // Index operator, get data at given index. No boundary check
    T& operator[] (const unsigned int i);       // Index operator, get data at given index. No boundary check
    DynamicArray& operator=(const DynamicArray& other); // Assignment
    DynamicArray& operator=(DynamicArray&& other);      // Move Assignment


    // Add iterator properties to class ---------------------------------------------------------------
    class iterator {                           // Local class for iterator
        T* iter{};                             // This will be the iterator 
        T* begin{};                            // For boundary check
        T* end{};                              // For boundary check

    public:                                    // Define alias names necessary for the iterator functionality
        using iterator_category = std::random_access_iterator_tag;
        using difference_type = std::ptrdiff_t;
        using value_type = T;
        using pointer = T*;
        using reference = T&;

        // Constructor
        iterator(T* const i, T* const b, T* const e);

        // Dereferencing
        reference operator *() const;
        pointer operator ->() const;

        // Aithmetic operations
        iterator& operator ++();
        iterator& operator --();
        iterator operator ++(int);
        iterator operator --(int);
        iterator operator +(const difference_type& n) const;
        iterator& operator +=(const difference_type& n);
        iterator operator -(const difference_type& n) const;
        iterator& operator -=(const difference_type& n);

        // Comparison
        bool operator != (const iterator& other) const;
        bool operator == (const iterator& other) const;
        bool operator < (const iterator& other) const;
        bool operator > (const iterator& other) const;
        bool operator <= (const iterator& other) const;
        bool operator >= (const iterator& other) const;

        // Reference and difference
        reference operator[] (const difference_type& n);
        difference_type operator-(const iterator& other) const;
    };

    // Begin and end function to initialize an iterator
    iterator begin() const;
    iterator end() const;

    // Working functions dealing with iterators. More may be added
    iterator erase(iterator pos);

};


// Default constructor. Allocate new memory
template <typename T>
inline DynamicArray<T>::DynamicArray() {
    data = new T[capacity];
}
// Constructor for certain size. Allocate new memory
template <typename T>
inline DynamicArray<T>::DynamicArray(const unsigned int size) : data(new T[size]), numberOfElements(0), capacity(size) {
}

// Copy constructor
template <typename T>
DynamicArray<T>::DynamicArray(const DynamicArray& other) {  // Copy constructor. Make a deep copy
    capacity = numberOfElements = other.numberOfElements;
    data = new T[capacity];                // Get memory, same size as other container
    for (size_t k = 0; k < other.numberOfElements; ++k)
        data[k] = other.data[k];           // Copy data
}

// Move constructor
template <typename T>
DynamicArray<T>::DynamicArray(DynamicArray&& other) {
    data = other.data;
    numberOfElements = other.numberOfElements;
    capacity = other.capacity;
    other.capacity = InitialCapacity;
    other.numberOfElements = 0;
    other.data = new T[capacity];;
}

// Range constructor
template <typename T>
template <class Iterator>
DynamicArray<T>::DynamicArray(Iterator begin, Iterator end) {
    data = new T[capacity];
    for (Iterator i = begin; i != end; ++i)
        push_back(*i);
}

// Construct from a const C-Style Array, like for example "Hello"
template <typename T>
template <int N>
DynamicArray<T>::DynamicArray(const T(&other)[N]) {
    capacity = numberOfElements = N;
    data = new T[capacity];                // Get memory, same size as other container
    for (size_t k = 0; k < N; ++k)
        data[k] = other[k];          // Copy data
}
// Construct from a C-Style Array
template <typename T>
template <int N>
DynamicArray<T>::DynamicArray(T(&other)[N]) {
    capacity = numberOfElements = N;
    data = new T[capacity];                // Get memory, same size as other container
    for (size_t k = 0; k < N; ++k)
        data[k] = other[k];          // Copy data
}

// Construct from an initializer list
template <typename T>
DynamicArray<T>::DynamicArray(const std::initializer_list<T>& list) {
    data = new T[capacity];
    for (const T& t : list) push_back(t);
}

// Destructor will release the dynamic allocated memory
template <typename T>
inline DynamicArray<T>::~DynamicArray() {
    delete[] data;
}         // Destructor: Release previously allocated memory

// Some houskeeping functions
template <typename T>
inline bool DynamicArray<T>::empty() const {
    return numberOfElements == 0;
}
template <typename T>
inline void DynamicArray<T>::clear() {
    numberOfElements = 0;
};    // Clear will not delete anything. Just set element count to 0

template <typename T>
inline unsigned int DynamicArray<T>::size() const {
    return numberOfElements;
} // How many elements are in the container

// Main workhorse for a dynamic array. 
// Store element, and alwaysprovide enough memory
template <typename T>
void DynamicArray<T>::push_back(const T& d) {               // Add a new element at the end
    if (numberOfElements >= capacity) {                     // Check, if capacity of this dynamic array is big enough
        capacity *= 2;                                      // Obviously not, we will double the capacity
        T* temp = new T[capacity];                          // Allocate new and more memory
        for (unsigned int k = 0; k < numberOfElements; ++k)
            temp[k] = data[k];                              // Copy data from old memory to new memory
        delete[] data;                                      // Release old memory
        data = temp;                                        // And assign newly allocated memory to old pointer
    }
    data[numberOfElements++] = d;                           // And finally, store the given data at the end of the container
}

// Operators for class ------------------------ ---------------------------------------------------------------
template <typename T>
inline typename T DynamicArray<T>::operator[] (const unsigned int i) const {
    return data[i];
}      // Index operator, get data at given index. No boundary check

template <typename T>
inline typename T& DynamicArray<T>::operator[] (const unsigned int i) {
    return data[i];
}  // Index operator, get data at given index. No boundary check

// Assignement operator. Make a deep copy
template <typename T>
DynamicArray<T>& DynamicArray<T>::operator=(const DynamicArray& other) {
    if (this != &other) {                                    // Prevent self-assignment
        delete[] data;                                       // Release any previosly existing memory
        capacity = numberOfElements = other.numberOfElements;// Take over capacity and number of elements from other container
        data = new T[capacity];                              // Get new memory, depending on size of other 
        for (unsigned int k = 0; k < numberOfElements; ++k)  // Copy other data
            data[k] = other.data[k];
    }
    return *this;
}
template <typename T>
DynamicArray<T>& DynamicArray<T>::operator=(DynamicArray&& other) {      // Move Assignment
    if (this != &other) {                                    // Prevent self-assignment
        data = other.data;
        numberOfElements = other.numberOfElements;
        capacity = other.capacity;
        other.capacity = InitialCapacity;
        other.numberOfElements = 0;
        other.data = new T[capacity];;
    }
    return *this;
}
// Implementation of iterator functions ---------------------------------------------------------------------
// COnstruction 
template <typename T>
inline DynamicArray<T>::iterator::iterator(T* const i, T* const b, T* const e) : iter(i), begin(b), end(e) {
};  // Constructor for the iterator

// Dereferencing
template <typename T>
inline typename DynamicArray<T>::iterator::reference DynamicArray<T>::iterator::operator *() const {
    return *iter;
}

template <typename T>
inline typename DynamicArray<T>::iterator::pointer DynamicArray<T>::iterator::operator ->() const {
    return iter;
}

// Arithmetic operations
template <typename T>
inline typename DynamicArray<T>::iterator& DynamicArray<T>::iterator::operator ++() {
    if (iter < end)
        ++iter;
    return *this;
}

template <typename T>
inline typename DynamicArray<T>::iterator& DynamicArray<T>::iterator::operator --() {
    if (iter > begin)
        --iter;
    return *this;
}

template <typename T>
typename DynamicArray<T>::iterator DynamicArray<T>::iterator::operator ++(int) {
    DynamicArray<T>::iterator tmp = *this;
    if (this->iter < end)
        ++(*this);
    return tmp;
}

template <typename T>
typename DynamicArray<T>::iterator DynamicArray<T>::iterator::operator --(int) {
    DynamicArray<T>::iterator tmp = *this;
    if (this->iter > begin)
        --(*this);
    return tmp;
}

template <typename T>
typename DynamicArray<T>::iterator DynamicArray<T>::iterator::operator +(const DynamicArray<T>::iterator::difference_type& n) const {
    DynamicArray<T>::iterator tmp = *this;
    DynamicArray<T>::iterator::difference_type k{ n };
    if (k > 0)
        while (k--)
            ++tmp;
    else
        while (k++)
            --tmp;
    return tmp;
}
template <typename T>
typename DynamicArray<T>::iterator& DynamicArray<T>::iterator::operator +=(const DynamicArray<T>::iterator::difference_type& n) {
    DynamicArray<T>::iterator::difference_type k{ n };
    if (k > 0)
        while (k--)
            ++* this;
    else
        while (k++)
            --* this;
    return *this;
}

template <typename T>
typename DynamicArray<T>::iterator DynamicArray<T>::iterator::operator- (const DynamicArray<T>::iterator::difference_type& n) const {
    DynamicArray<T>::iterator tmp = *this;
    DynamicArray<T>::iterator::difference_type k{ n };
    if (k > 0)
        while (k--)
            --tmp;
    else
        while (k++)
            ++tmp;
    return tmp;
}

template <typename T>
typename DynamicArray<T>::iterator& DynamicArray<T>::iterator::operator -=(const typename DynamicArray<T>::iterator::difference_type& n) {
    DynamicArray<T>::iterator::difference_type k{ n };
    if (k > 0)
        while (k--)
            --* this;
    else
        while (k++)
            ++* this;
    return *this;
}

// Comparison functions
template <typename T>
inline typename DynamicArray<T>::iterator::reference DynamicArray<T>::iterator::operator[] (const typename DynamicArray<T>::iterator::difference_type& n) {
    return *(iter + n);
};

template <typename T>
inline bool DynamicArray<T>::iterator::operator != (const iterator& other) const {
    return iter != other.iter;
}

template <typename T>
inline bool DynamicArray<T>::iterator::operator == (const iterator& other) const {
    return iter == other.iter;
}

template <typename T>
inline bool DynamicArray<T>::iterator::operator < (const iterator& other) const {
    return iter < other.iter;
}
template <typename T>
inline bool DynamicArray<T>::iterator::operator > (const iterator& other) const {
    return iter > other.iter;
}  // Comparison

template <typename T>
inline bool DynamicArray<T>::iterator::operator <= (const iterator& other) const {
    return iter <= other.iter;
}  // Comparison

template <typename T>
inline bool DynamicArray<T>::iterator::operator >= (const iterator& other) const {
    return iter >= other.iter;
}  // Comparison

// Delta 
template <typename T>
inline typename DynamicArray<T>::iterator::difference_type DynamicArray<T>::iterator::operator-(const typename DynamicArray<T>::iterator& other) const {
    return iter - other.iter;
}

// ------------------------------------------------------------------------
// Get iterators for dynamic array
template <typename T>
inline typename DynamicArray<T>::iterator DynamicArray<T>::begin() const {
    return iterator(data, data, data + numberOfElements);
}

template <typename T>
inline typename DynamicArray<T>::iterator DynamicArray<T>::end() const {
    return iterator(data + numberOfElements, data, data + numberOfElements);
}

// ------------------------------------------------------------------------
// Any other functions for dynamic array
template <typename T>
typename DynamicArray<T>::iterator DynamicArray<T>::erase(typename DynamicArray<T>::iterator pos) {
    iterator result{ pos };
    if (pos != end()) {
        while (pos != end()) {
            *pos = *(pos + 1);
            ++pos;
        }
        ++result;
        --numberOfElements;
    }
    return result;
}


// --------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------

// Using the dynamic array as a String, by using char as the content
using String = DynamicArray<char>;

// Some overloads for operators for easier handling
std::istream& operator >> (std::istream& is, String& s) {
    s.clear();
    char c{};
    is >> std::ws;
    while (is.peek() != EOF and is.get(c) and not isspace(c)) {
        s.push_back(c);
    }
    if (not s.empty()) s.push_back(0);
    return is;
}
std::ostream& operator << (std::ostream& os, const String& s) {
    std::ostringstream oss;
    for (char c : s)  if (c != '\0') oss << c;
    return os << oss.str();
}
bool operator < (const String& s1, const String& s2) {
    unsigned int length{ (s1.size() < s2.size()) ? s1.size() : s2.size() };
    for (unsigned int k{}; k < length; ++k) {
        if (s1[k] == s2[k]) continue;
        if (s1[k] < s2[k]) return true;
        return false;
    }
    return false;
}
bool operator == (const String& s1, const String& s2) {
    if (s1.size() != s2.size()) return false;
    for (unsigned int k{}; k < s1.size(); ++k) {
        if (s1[k] != s2[k]) return false;
    }
    return true;
}
bool operator != (const String& s1, const String& s2) { return not (s1 == s2); }

A M
  • 14,694
  • 5
  • 19
  • 44