0

I am building a custom string class. However, the user inputs give different output from the assigned-value one. For example: With the assigned values:

    #include"custom_string.h"
    int main() {
        string a = "Hello";
        string b = "World";
        string c = a + b;
        std::cout << "C string is: " << c << std::endl;
        std::cout << "Length of C string is: " << c.length() << std::endl;
        system("pause");
        return 1;
}

Then the output would be:

C string is: HelloWorld

Length of C string is: 10

In user input case:

    #include"custom_string.h"
        int main() {
            string a;
            string b;
            string c = a + b;
            std::cout << "Input a string: " << std::endl;
            std::cin >> a;
            std::cout << "Input b string: " << std::endl;
            std::cin >> b;
            std::cout << "C string is: " << c << std::endl;
            std::cout << "Length of C string is: " << c.length() << std::endl;
            system("pause");
            return 0;
}

Then the output would be:

Input a string:

Hello

Input b string:

World

C string is:

Length of C string is: 0

Here is the code source of "custom_string.h":

 #ifndef _STRING
        #define _STRING
        #include<iostream>
        #include<cstring>
        #define MAX 99999
        class string {
        private:
            char* s = nullptr; //Member of custom string
            unsigned int size = 0; // length of string (including '\0')
        public:
            string();
            ~string() { delete s; };
            string(char* );
            string(const char* );
            string(const string&);
            friend std::istream& operator >> (std::istream&, string&);
            friend std::ostream& operator << (std::ostream&, string&);
            friend string operator +(string, string);
            string& operator = (const string&);
            string& operator = (const char&);
            unsigned int length();
            char* output() const{
                return s;
            }
        };
        #endif

    string::string() :s{ nullptr } {
        size = 1;
        s = new char[size];
        s[0] = '\0';
    }
    string::string(char* source) {
        if (source == nullptr) {
            size = 1;
            s = new char[size];
            s[0] = '\0';
        }
        else {
            size_t i = 0;
            while (source[i] != '\0') { //To remove NULL/redundant elements of source array from istream assignment
                i++;
            }
            size = i + 1;
            s = new char[size];
            s[size - 1] = '\0';
            for (size_t k = 0; k < size - 1; k++) {
                s[k] = source[k];
            }
        }
    }
    string::string(const char* source) {
        if (source == nullptr) {
            size = 1;
            s = new char[size];
            s[0] = '\0';
        }
        else {
            size = strlen(source) + 1;
            s = new char[size];
            s[size - 1] = '\0';
            for (size_t k = 0; k < (size - 1); k++) {
                s[k] = source[k];
            }
        }
    }
    string::string(const string& t) {
        size = t.size;
        s = new char[size];
        for (size_t k = 0; k < size; k++) {
            s[k] = t.s[k];
        }
    }
    string& string::operator=(const string& source) {
        if (source.s == s) {
            return *this;
        }
        else {
            delete[] s;
            size = source.size;
            s = new char[size];
            for (size_t k = 0; k < size; k++) {
                s[k] = source.s[k];
            }
            return *this;
        }
    }
    string& string::operator=(const char&source) {
        const char* t = &source;
        if (t == nullptr) {
            size = 1;
            s = new char[size];
            s[0] = '\0';
        }
        else {
            size = strlen(t) + 1;
            s = new char[size];
            s[size - 1] = '\0';
            for (size_t k = 0; k < (size - 1); k++) {
                s[k] = t[k];
            }
        }
        return* this;
    }
    string operator +(string a, string b) {
        if (a.s == nullptr ) { return b; }
        if (b.s == nullptr ) { return a; }
        string t;
        size_t k = 0;
        size_t l = 0;
        t.size = (a.size + b.size) - 1;
        t.s = new char[t.size];
        while (a.s[k] != '\0' && k < a.size && k < t.size) {
            t.s[k] = a.s[k];
            k++;
        }
        while (k < t.size && l < b.size) {
            t.s[k] = b.s[l];
            k++;
            l++;
        }
        return t;
    }
    std::istream& operator >> (std::istream& is, string& source) {
        char* t = new char[MAX];
        is >> t;
        source = string{ t };
        delete[] t;
        return is;
    }
    
    std::ostream& operator << (std::ostream& os, string& source) {
        os << source.output();
        return os;
    }
unsigned int string::length() {
    return (size - 1); //Ignore the '\0' character
}

I do not know what makes the difference between to cases. Maybe I have missed some necessary commands.

Edit: I have known assigning c after input a and b will solve the problem. However, I don't want to do so because I want to separate assigning part and complying part in order to have neat code. Is there any way to fix error without assigning c after input a and b?

Eureka
  • 35
  • 1
  • 6
  • 1
    In the second case, you are assigning `c` ***before*** `a` and `b` have any values. – Yksisarvinen May 12 '21 at 14:39
  • How can I solve this without assigning c after a and b? You see, I want to separate the assigning part and complying part in order to be neat – Eureka May 12 '21 at 14:42
  • The order in which you do things like initializing or assigning to variables matter. If you have two statements `A;` and `B;`, in the exact order `A; B;`, then `A` will be executed *first* before `B`. – Some programmer dude May 12 '21 at 14:42
  • 1
    Why not simply define the variable `c` *after* you have read the input? – Some programmer dude May 12 '21 at 14:42
  • @Eureka It is theoretically possible, but you don't want to do that. You would need to make your `string` class more of a `string_view` - make `operator+` return some sort of references to the strings it adds (instead of actually concatenating) and implement some view class to iterate over such references. It definitely breaks the rule of least astonishment. Instead of a string, who is an owner of its data, you get some weird view, which doesn't own anything and only has references to other strings. – Yksisarvinen May 12 '21 at 14:48
  • On an unrelated note, any symbol beginning with an underscore and followed by an uppe-case letter (like e.g. `_STRING`) is *reserved* and should not be defined by your code. See [What are the rules about using an underscore in a C++ identifier?](https://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-c-identifier) – Some programmer dude May 12 '21 at 14:50
  • Definitely don't name your custom type `string`. There will be name collisions when `using namespace std;` is used, either globally or local to any functions. – Casey May 12 '21 at 15:21
  • You're using the wrong `delete`. You consistently use `new[]` but you don't use `delete[]` in your destructor. – Casey May 12 '21 at 15:23
  • `Edit: I have known assigning c after input a and b will solve the problem. However, I don't want to do so because I want to separate assigning part and complying part in order to have neat code. Is there any way to fix error without assigning c after input a and b?` That's not how C++ works. – Casey May 12 '21 at 15:24

1 Answers1

1
 #include"custom_string.h"
        int main() {
            string a;
            string b;
            std::cout << "Input a string: " << std::endl;
            std::cin >> a;
            std::cout << "Input b string: " << std::endl;
            std::cin >> b;
            string c = a + b;
            std::cout << "C string is: " << c << std::endl;
            std::cout << "Length of C string is: " << c.length() << std::endl;
            system("pause");
            return 0;
}
Yead
  • 91
  • 5