Problem with string class

Hello everyone. I've been trying to create my own string class, but I've encountered some problems while reading the input(I guess it just went to an infinite loop). Here is the code. If you have any suggestions as to how I could create the read(istream &in) and the print(ostream &out) files, it would be appreciated, thanks.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include<iostream>
#include<fstream>

using namespace std;

class str{
public:
int currPlace;
char *currStr;
str(){
currPlace = 0;
currStr = new char [1];
}
void read(istream &in){
char input;
currPlace = 1;
/*I thought of storing the input to temporary array, and then copy that temporary 

array to the other, with the last input, because I would resize the normal array each 

time a new character was inputted, not very practical, but it is the only method i 

could think of. It doesn't even work, since it just goes to an infinite loop */
while (in>>input){
    char *lol;
    lol = new char [currPlace-1];
    for (int i=0; i<=currPlace-1; i++){
        lol [i] = currStr[i];
    }
    currStr = new char[currPlace];
    for (int i=0; i<=currPlace-1; i++){
        currStr[i] = lol[i];
    }
    currStr[currPlace] = input;
    currPlace++;
}
}
void print(ostream &out){
for (int i=1; i<=currPlace; i++){
    out<<currStr[i];
}
}
char operator [] (int a){
return currStr[a-1];
}
};

int main(){
str test;
test.read(cin);
test.print(cout);
}
Last edited on
closed account (E0p9LyTq)
C++ has a very robust string class/template in the STL, why not use that?

A while back I ran across a very rudimentary, C++98(?) version of a custom string class (and driver program):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
#include <iostream>
#include <cstring>

// rudimentary string class
class String
{
public:
   String();
   String(const char* const);
   String(const String&);
   ~String();

public:
   char& operator[](unsigned short offset);
   char operator[](unsigned short offset) const;
   String operator+(const String&);
   void operator+=(const String&);
   String& operator= (const String&);

public:
   unsigned short GetLen() const { return itsLen; }
   const char* GetString() const { return itsString; }

private:
   // private constructor
   String(unsigned short);

private:
   char* itsString;
   unsigned short itsLen;
};


int main()
{
   String s1("initial test");
   std::cout << "S1:\t\t" << s1.GetString() << "\n";

   char* temp = "Hello World";
   s1 = temp;
   std::cout << "S1:\t\t" << s1.GetString() << "\n";

   char tempTwo[20];
   strcpy(tempTwo,"; nice to be here!");
   s1 += tempTwo;
   std::cout << "tempTwo:\t" << tempTwo << "\n";
   std::cout << "S1:\t\t" << s1.GetString() << "\n";

   std::cout << "S1[4]:\t\t" << s1[4] << "\n";
   s1[4]='x';
   std::cout << "S1:\t\t" << s1.GetString() << "\n";

   std::cout << "S1[999]:\t" << s1[999] << "\n";

   String s2(" Another string");
   String s3;
   s3 = s1 + s2;
   std::cout << "S3:\t\t" << s3.GetString() << "\n";

   String s4;
   s4 = "Why does this work?";
   std::cout << "S4:\t\t" << s4.GetString() << "\n";
}


// default constructor creates string of 0 bytes
String::String()
{
   itsString = new char[1];
   itsString[0] = '\0';
   itsLen = 0;
}


// private (helper) constructor, used only by
// class methods for creating a new string of
// required size. Null filled.
String::String(unsigned short len)
{
   itsString = new char[len + 1];
   for (unsigned short i = 0; i <= len; i++)
   {
      itsString[i] = '\0';
   }
   itsLen = len;
}


// Converts a character array to a String
String::String(const char* const cString)
{
   itsLen = static_cast<unsigned short> (strlen(cString));
   itsString = new char[itsLen + 1];
   for (unsigned short i = 0; i < itsLen; i++)
   {
      itsString[i] = cString[i];
   }
   itsString[itsLen]='\0';
}


// copy constructor
String::String (const String & rhs)
{
   itsLen = rhs.GetLen();
   itsString = new char[itsLen + 1];
   for (unsigned short i = 0; i < itsLen; i++)
   {
      itsString[i] = rhs[i];
   }
   itsString[itsLen] = '\0';
}


// destructor, frees allocated memory
String::~String ()
{
   delete[] itsString;
   itsLen = 0;
}


// operator equals, frees existing memory
// then copies string and size
String& String::operator=(const String& rhs)
{
   if (this == &rhs)
   {
      return *this;
   }
   delete[] itsString;
   itsLen = rhs.GetLen();
   itsString = new char[itsLen + 1];
   for (unsigned short i = 0; i < itsLen; i++)
   {
      itsString[i] = rhs[i];
   }
   itsString[itsLen] = '\0';
   return *this;
}


// nonconstant offset operator, returns
// reference to character so it can be
// changed!
char& String::operator[](unsigned short offset)
{
   if (offset > itsLen)
   {
      return itsString[itsLen-1];
   }
   else
   {
      return itsString[offset];
   }
}


// constant offset operator for use
// on const objects (see copy constructor!)
char String::operator[](unsigned short offset) const
{
   if (offset > itsLen)
   {
      return itsString[itsLen - 1];
   }
   else
   {
      return itsString[offset];
   }
}


// creates a new string by adding current
// string to rhs
String String::operator+(const String& rhs)
{
   unsigned short  totalLen = itsLen + rhs.GetLen();
   String temp(totalLen);
   unsigned short i;
   for (i = 0; i < itsLen; i++)
   {
      temp[i] = itsString[i];
   }
   for (unsigned short j = 0; j < rhs.GetLen(); j++, i++)
   {
      temp[i] = rhs[j];
   }
   temp[totalLen] = '\0';
   return temp;
}


// changes current string, returns nothing
void String::operator+=(const String& rhs)
{
   unsigned short rhsLen = rhs.GetLen();
   unsigned short totalLen = itsLen + rhsLen;
   String  temp(totalLen);
   unsigned short i;
   for (i = 0; i < itsLen; i++)
   {
      temp[i] = itsString[i];
   }
   for (unsigned short j = 0; j < rhs.GetLen(); j++, i++)
   {
      temp[i] = rhs[i - itsLen];
   }
   temp[totalLen] = '\0';
   *this = temp;
}
@FurryGuy

I usually use that, but I wanted to make one for practice.
closed account (E0p9LyTq)
I figured that is what you were doing. The custom string class I posted might be a good start, without overriding the >> and << iostream operators. It works, but it is messy.

The code isn't mine, I borrowed it from some website that no longer exists.
closed account (D80DSL3A)
Hi. Is your read function meant to read the entire content (not just 1 line) of the stream to an str ?
If so, there is a std::istream function which gives the # of characters in the stream, so you can allocate enough space up front. See http://www.cplusplus.com/reference/istream/istream/
Here's what I have, as a global (not member) function:
1
2
3
4
5
6
7
8
9
10
11
12
std::istream& read( std::istream& is, myStr& s )
{
    s.clear();// a member function of myStr class
    // get length of file:
    is.seekg (0, is.end);
    s.sz = is.tellg();// # chars in stream
    is.seekg (0, is.beg);// reset to beginning
    s.pStr = new char[s.sz+1];// I null term, so I need 1 extra
    is.read( s.pStr, s.sz );
    s.pStr[s.sz] = '\0';
    return is;
}

I think this adapts to your case here:
1
2
3
4
5
6
7
8
9
10
11
std::istream& str::read( std::istream& is )
{
   if( currStr ) delete [] currStr;
    // get length of file:
    is.seekg (0, is.end);
    currPlace = is.tellg();// # chars in stream
    is.seekg (0, is.beg);// reset to beginning
    currStr = new char[currPlace];// no extra for '\0'
    is.read( currStr, currPlace );
    return is;
}

The harder one for me was std::istream& getline( std::istream&, char delim = '\n' ); due to inability to know the stream size before reading from it. I used an approach similar to what you're attempting, but with a larger char array to serve as an input buffer.
Here's mine, again as a global function:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
std::istream& getline( std::istream& is, myStr& s, char delim = '\n' )
{
    const int buffSz = 6;// make bigger after testing
    char buff[buffSz];

    s.clear();
   // read into buff. Append buff to s
    while( is.get( buff, buffSz, delim ) ) s += buff;

    std::cin.clear();
    std::cin.ignore( 256, '\n' );

    return is;
}

That code uses a couple of function you may not have written yet.
1. A constructor taking const char* as an argument. A string should be constructable from a string literal, ie
str myName("Joe Dirt");
This is a good ctor to have:
str(const char*);
2. Overload operator +=, or write an append function.
Then you can write:
while( is.get( buff, buffSz, '\n' ) ) s += buff;// relies on new ctor
or
while( is.get( buff, buffSz, '\n' ) ) s.append(buff);
Last edited on
@fun2code

Thanks, this is really useful. And yes, initially I wanted to read the entire content before I did something else, since it seemed simpler.
Topic archived. No new replies allowed.