I'm currently in the second sequence of a C++ course. I'm building my own string class using c-strings & dynamic memory.
I have a majority of my code working. I'm reading in a file and putting each word in a vector of my class type "ATString". We are supposed to combine 5 of the words read in, into a jumbo word, and putting that into another vector. When I use the debugger to step through my code, I see the words combining and it is copying the words over to the new ATString variable "tempJumbo". After a few lines run, the program crashes and tells me my program has triggered a breakpoint, leaving me on line 96 of the addition operator function.
Here the operator+ definition:
ATString ATString::operator+ (ATString& string1) {
int newSize = length() + string1.length();
ATString newString;
if (newSize > 20) { // increase capacity if size is greater than 20 characters
newString.increase_cap();
}
else {
cap = 20;
}
newString.strPtr = new char[newString.cap];
newString.end = newSize;
for (int i = 0; i < length(); i++) {
newString[i] = strPtr[i];
for (int j = length(); j < newSize; j++) {
newString[j] = string1.strPtr[j-end];
}
return newString;
}
And here is main, reading in the file and attempting to combine the words into a jumbo word.
int main() {
vector<ATString> words(100);
vector<ATString> lines(100); // calls default constructor 100 times
ifstream fin("infile3.txt");
int index = 0;
int wordCount = 0;
//READ
if (fin.fail()) {
cout << "Couldn't open infile2.txt" << endl;
system("pause");
exit(1);
}
while (!fin.eof()) {
fin >> words[index];
index++;
}
wordCount = index;
words.resize(wordCount);
//COMBINE 5 WORDS INTO ONE JUMBO
ATString tempJumbo;
int j = 0;
for (int i = 0;i < wordCount; i++) {
tempJumbo = words[i] + words[i+1] + words[i+2] + words[i+3] + words[i+4];
lines[j] = tempJumbo; // putting big word into lines vector
tempJumbo = " "; //resetting variable to hold jumbo word?
i = i + 4;
j++;
if (i == wordCount) {
break;
}
}
return 0;
}
I am also have issues with the destructor I wrote.. it's pretty simple and when I have this activated, it triggers an error as well and crashes the program. Not sure what is going here.
ATString::~ATString() {
delete strPtr;
}
Below is my header file:
#ifndef ATSTRING_H
#define ATSTRING_H
#include <istream>
using namespace std;
class ATString {
public:
ATString();// default constructor
ATString(const char* cstr); // cstring constructor
ATString(const ATString& argstr); // copy constructor
~ATString(); // destructor
ATString& operator = (const ATString& objToCopy); // assignment operator =
ATString operator + (ATString& string1); // addition operator +
int length() const;
int capacity() const;
void increase_cap();
char& operator [] (int index); // indexing operator
const char& operator [] (int index) const; // const indexing operator
bool operator <(const ATString& argstr) ;
bool operator > (const ATString& argstr) ;
bool operator ==(const ATString& argstr);
friend istream& operator >> (istream& inStrm, ATString& argstr); // extraction operator
friend const ostream& operator << (ostream& outStrm, const ATString& argstr); // insertion opertator
private:
char* strPtr;
int end;
int cap;
int compareTo(const ATString& argStr);
};
#endif
THANK YOU!
How big is your input file? If it is 100 words or more, then your index runs out of bounds on the line, when i = 96, i.e. trying to get words[100] element.
tempJumbo = words[i] + words[i+1] + words[i+2] + words[i+3] + words[i+4];
Related
I realized string class MyString. Here is code:
#include <iostream>
#include <cstring>
using std::cout;
using std::endl;
class MyString{
private:
char * content;
int length;
void copy(const MyString & source);
public:
MyString();
MyString(const char * source);
~MyString();
MyString(const MyString & source);
void print(void);
MyString & operator = (const MyString &source);
friend std::ostream & operator << (std::ostream & out, const MyString& towrite);
friend std::istream & operator >> (std::istream & in, MyString & toread);
};
MyString::MyString(){
content = new char[1];
content[0] = '\0';
length = 0;
}
MyString::MyString(const char *source){
length = strlen(source);
content = new char[length + 1];
strcpy(content, source);
}
MyString::~MyString(){
delete[] content;
}
void MyString::copy(const MyString & source){
length = source.length;
content = new char[length + 1];
strcpy(content, source.content);
}
MyString::MyString(const MyString & source){
copy(source);
}
void MyString::print(void){
cout << "" << content << endl;
}
MyString &MyString::operator=(const MyString &source){
copy(source);
return *this;
}
std::ostream & operator<<(std::ostream & out,const MyString& towrite){
out << towrite.content;
return out;
}
std::istream & operator >> (std::istream & in, MyString & toread){
int length;
std::cout << "Enter length of word: " << endl;
std::cin >> length;
toread.length = length;
toread.content = new char[toread.length+1];
for (int i = 0; i < toread.length; i++){
in >> toread.content[i] ;
}
toread.content[toread.length] = '\0';
return in;
}
My question is related to overloaded operator >>.
For this main program:
int main(){
MyString word;
std::cout<<"Enter some word: "<<endl;
std::cin>>word;
std::cout<<"Your entered: "<<word<<endl;
}
this is output:
Enter some word:
Enter length of word:
5
stack
Your entered: stack
Process returned 0 (0x0) execution time : 8.313 s
Press any key to continue.
It prints correctly string user entered, but it doesn't "mimic" original string class on the way I want. Here is why.
In case of using C++ string class:
int main(){
std::string word;
std::cout<<"Enter some word: "<<endl;
std::cin>>word;
std::cout<<"Your entered: "<<word<<endl;
}
user doesn't need to enter length of word. Can I achieve this with my class?
EDIT1:
I did it on this way:
std::istream & operator >> (std::istream & in, MyString & toread){
char *temp;
temp = new char[100];
char c;
int i = 0;
while(c != '\n'){
c = getchar();
temp[i++] = c;
}
temp[i] = '\0';
int length = i-1;
toread.length = length;
toread.content = new char[toread.length+1];
for(int i = 0 ; i < toread.length ; i++){
toread.content[i] = temp[i];
}
delete [] temp;
toread.content[toread.length+1]='\0';
}
It works as it should. However, I get warning because I didn't return "in":
||=== Build: Debug in fdsfsdf (compiler: GNU GCC Compiler) ===|
C:\Users\hae\Desktop\fdsfsdf\main.cpp||In function 'std::istream& operator>>(std::istream&, MyString&)':|
C:\Users\hae\Desktop\fdsfsdf\main.cpp|137|warning: no return statement in function returning non-void [-Wreturn-type]|
||=== Build finished: 0 error(s), 1 warning(s) (0 minute(s), 4 second(s)) ===|
||=== Run: Debug in fdsfsdf (compiler: GNU GCC Compiler) ===|
Here's a stripped down version of a similar class I wrote a long time ago. It's an antique, but it should work, and solves some of the issues with your class.
class charray {
public:
charray();
~charray();
charray(const charray&);
charray(const char*);
charray& operator=(const charray&);
charray& operator=(const char*);
void swap(charray&);
const char* c_str() const
{ return m_elem; }
unsigned int size() const
{ return m_size; }
private:
void m_resize(unsigned int size);
char* m_elem;
unsigned int m_size;
};
// private.
void charray::m_resize(unsigned int size)
{
char* elem = new char[size+1];
memcpy(elem, m_elem, std::min(m_size, size));
elem[size] = '\0';
delete [] m_elem;
m_elem = elem;
m_size = size;
}
// public.
charray::charray()
: m_elem(0), m_size(0)
{
m_resize(0);
}
charray::~charray()
{
delete [] m_elem;
}
charray::charray(const charray& src)
: m_elem(0), m_size(0)
{
unsigned int size = src.size();
m_resize(size);
memcpy(m_elem, src.c_str(), size);
}
charray::charray(const char* src)
: m_elem(0), m_size(0)
{
unsigned int size = std::strlen(src);
m_resize(size);
memcpy(m_elem, src, size);
}
charray& charray::operator=(const charray& rhs)
{
charray temp(rhs);
this->swap(temp);
return *this;
}
charray& charray::operator=(const char* rhs)
{
charray temp(rhs);
this->swap(temp);
return *this;
}
void charray::swap(charray& b)
{
std::swap(m_elem, b.m_elem);
std::swap(m_size, b.m_size);
}
Here is what you're probably most interested in. Pay close attention to the details. When dealing with memory directly, the difference between a working implementation and a broken one is often very subtle.
Note: The operators are not friends. They do not access private data.
std::ostream& operator<<(std::ostream& out, const charray& in)
{
return out << in.c_str();
}
std::istream& operator>>(std::istream& in, charray& out)
{
// verify no errors are set, flush tied streams, strip leading
// whitespace.
std::istream::sentry sentry(in);
if (!sentry)
return in;
unsigned int size = 0;
unsigned int tail = 0;
char* temp = 0;
int next; // #note int not char (to record EOF).
while ((next = in.get()) != in.eof() && !std::isspace(next)) {
// if temp buffer is exhausted, then double the buffer size.
// (base size of 16).
if (tail == size) {
unsigned int newsize = std::max(2*size, 16u);
char* newtemp = new char[newsize+1];
memcpy(newtemp, temp, size);
delete [] temp;
temp = newtemp;
size = newsize;
}
temp[tail++] = next;
}
// #note because the stream is prepeared with istream::sentry, there
// will be at least one non-whitespace character in the stream.
assert(temp != 0);
temp[tail] = '\0';
out = temp;
delete [] temp;
return in;
}
A much easier and safer way to do the exact same thing,
#include <vector>
std::istream& operator>>(std::istream& in, charray& out)
{
std::istream::sentry sentry(in);
if (!sentry)
return in;
std::vector<char> temp;
int next;
while ((next = in.get()) != in.eof() && !std::isspace(next))
temp.push_back(next);
temp.push_back('\0');
out = &temp[0];
return in;
}
Edit
The above is outmoded (pre C++11). A modern implementation would likely handle construction and assignment differently. Here are updated versions of those methods,
Note: The method m_resize is gone. Everything is handled through constructors.
charray::charray(const char* src, unsigned int size)
: m_elem{ new char[size+1]{} }, m_size{ size }
{
std::copy(src, src + size, m_elem);
}
charray::charray()
: charray(nullptr, 0)
{}
charray::charray(const charray& src)
: charray(src.m_elem, src.m_size)
{}
charray::charray(const char* src)
: charray(src, std::strlen(src))
{}
charray::charray(charray&& src)
: m_elem{ src.m_elem }, m_size{ src.m_size }
{
src.m_elem = nullptr;
src.m_size = 0;
}
// handle both move and copy assignment.
charray& charray::operator=(charray rhs)
{
this->swap(rhs);
return *this;
}
Hope this helps. Good luck.
I have a problem with operator>> when I try to use it in my class String. I need to make a string from keyboard, but all my tries make fails. Help me, please.
When I try to turn in this code, I always have a crash.
#include<iostream>
#include<fstream>
using namespace std;
class myString
{
public:
friend std::ostream& operator<< (std::ostream& out, const myString& other);
friend std::istream& operator>> (std::istream& in, myString& other);
myString()
{
str = nullptr;
length = 0;
}
myString(const char* str)
{
length = strlen(str);
this->str = new char[length + 1];
for (int i = 0; i < length; i++)
{
this->str[i] = str[i];
}
this->str[length] = '\0';
}
~myString()
{
delete[] this->str;
}
myString(const myString& other)
{
length = strlen(other.str);
this->str = new char[length + 1];
for (int i = 0; i < length; i++)
{
this->str[i] = other.str[i];
}
this->str[length] = '\0';
}
myString& operator =(const myString& other)
{
if (this->str != nullptr)
{
delete[] str;
}
length = strlen(other.str);
this->str = new char[length + 1];
for (int i = 0; i < length; i++)
{
this->str[i] = other.str[i];
}
this->str[length] = '\0';
return *this;
}
myString& operator =(const char* other)
{
if (this->str != nullptr)
{
delete[] str;
}
length = strlen(other);
this->str = new char[length + 1];
for (int i = 0; i < length; i++)
{
this->str[i] = other[i];
}
this->str[length] = '\0';
return *this;
}
myString& operator +=(myString const& other) {
return (this->operator=(this->operator+(other)));
}
myString operator +(const myString& other)
{
myString newStr;
int thisLength = strlen(this->str);
int otherLength = strlen(other.str);
newStr.length = thisLength + otherLength;
newStr.str = new char[thisLength + otherLength + 1];
int i = 0;
for (; i < thisLength; i++)
{
newStr.str[i] = this->str[i];
}
for (int j = 0; j < otherLength; j++, i++)
{
newStr.str[i] = other.str[j];
}
newStr.str[thisLength + otherLength] = '\0';
return newStr;
}
void Print()
{
cout << str;
}
int Length()
{
return length;
}
bool operator ==(const myString& other)
{
if (this->length != other.length)
{
return false;
}
for (int i = 0; i < this->length; i++)
{
if (this->str[i] != other.str[i])
{
return false;
}
}
return true;
}
bool operator !=(const myString& other)
{
return !(this->operator==(other));
}
char& operator [](int index)
{
return this->str[index];
}
private:
char* str;
int length;
};
ostream& operator<< (ostream& out, const myString& other)
{
out << other.str;
return out;
}
istream& operator >> (istream& in, myString& other)
{
in.getline(other.str, sizeof(other.str));
return in;
}
int main()
{
myString str_3;
cin >> str_3;
cout << str_3;
return 0;
}
sizeof(other.str) is not the size of allocated buffer but the size of the pointer.
other.str is initialized to nullptr in the constructor, so it will try to read into invalid place and it will lead to Segmentation Fault.
Changing the constructor to
myString()
{
str = new char[1];
length = 0;
}
to keep the buffer size always length + 1 and changing the operator>> to
istream& operator >> (istream& in, myString& other)
{
in.getline(other.str, other.length + 1);
return in;
}
will prevent it from getting Segmentation Fault.
This won't solve the problem that the reading will be limited to length of already assigned string.
Improving the behavior will require other modifications like reading characters one-by-one and allocating buffer according to the size read.
You are trying to read to a pointer initialized with nullptr and you use sizeof() of that pointer when you have length data member. Since you already defined assignment operator for char array you can just use it:
istream& operator >> (istream& in, myString& other)
{
char buffer[256];
in.getline(buffer, sizeof(buffer));
other = buffer;
return in;
}
note buffer size is arbitrary here, real application should handle different sizes and grow on need, but that would require more advanced string implementation than yours. For your simplified solution which looks like student task this could be fine.
Note: your operator<< also has issue, if you call it on default constructed object it would lead to UB, as std::ostream does not accept nullptr on const char *. Simple check for nullptr would be sufficient there:
ostream& operator<< (ostream& out, const myString& other)
{
if(other.str)
out << other.str;
return out;
}
Ironically you did check for nullptr for delete[] which is not necessary.
You do not allocate space for your data member str in the default constructor. So when you write
myString str_3;
cin >> str_3;
then your code in operator>> (i.e. in.getline(other.str, sizeof(other.str));) will write to memory that you did not allocate before).
Make sure that str provides enough allocated memory before writing to it, e.g. by using alloc or realloc.
i am trying to overload operators << >> != == = and [] for Array class.
The app crashes on run, though no compilation errors are shown.
what could possibly be wrong? IDE used dev c++
Here's array.h
#ifndef ARRAY_H
#define ARRAY_H
#include <iostream>
using namespace std;
class Array{
friend ostream & operator << ( ostream &, const Array & );
friend istream & operator >> ( istream &, Array &);
private:
int size;
int * ptr;
public:
Array ( int = 10 );
Array ( const Array & ); //copy constructor
~Array ();
const Array &operator=( const Array & );
bool operator == ( const Array & ) const;
bool operator != ( const Array & ) const;
const int operator [] (int) const;
int getSize() const;
};
#endif
and now array.cpp
#include <iostream>
using namespace std;
#include "array.h"
Array::Array (int sze ){ //default constructor edited
size = (sze > 0 ? sze : 10);
ptr = new int [ size ];
for (int i = 0; i < size; i++)
ptr[ i ] = 0; //initial values
}
Array::Array (const Array & arr ): size(arr.size){
ptr = new int [size];
for ( int i = 0; i< size; i++)
ptr [ i ] = arr.ptr [ i ];
}
Array::~Array(){
delete [] ptr;
}
const Array &Array :: operator= ( const Array & right){//IMPO
if(&right != this){ //edited self assignment test
if(size != right.size){//diff sized arrays
delete [] ptr; //reclaim space
size = right.size;
ptr = new int [ size ]; //space created
}
}
for(int i=0; i<size; i++)
ptr[ i ] = right.ptr[ i ];
return *this; //enables cascading a=b=c
}
bool Array::operator == ( const Array & right) const{
if ( size != right.size )
return false;
for ( int i =0; i < size; i++ ){
if ( ptr [ i ] != right.ptr[ i ] )
return false;
}
return true;
}
bool Array::operator != ( const Array & right ) const{ //edited
return ! (*this == right);
}
const int Array::operator [] (int subscript) const{
if(subscript >=0 && subscript < size)
return ptr[ subscript ];
}
int Array::getSize() const{ return size; }
//friend functions not in .h
ostream & operator << ( ostream & output, const Array & array){
for (int i = 0; i < array.size; i++)
output << array.ptr[i] ;
}
istream & operator >> ( istream & input, Array & array){
for (int i = 0; i < array.size; i++)
input >> array.ptr[i];
}
now main.cpp
#include <cstdlib>
#include <iostream>
#include "array.h" // " " not <>
using namespace std;
int main(int argc, char *argv[])
{
Array a1(7),a2 (-1),a4; //changed a2
cout<<"Input "<<a1.getSize()<<" integers for Array object a1 and "<<a2.getSize()<<" integers for Array objecta2\n";
cin>>a1>>a2;
cout<<"a1 and a2 are\n";
cout<<a1<<endl<<a2;
cout<<"a1!=a2 : "<<(a1!=a2)<<endl;
cout<<"a1 ==a2: "<<(a1==a2)<<endl;
cout<<"Printing a1[5] : "<<a1[5]<<endl;
Array a3(a1);
a4 = a3;
system("PAUSE");
return EXIT_SUCCESS;
}
You have to reserve memory for ptr in the constructor.
Array::Array (int size ){ //default constructor
size = (size > 0 ? size : 10);
ptr = new int [size]; // ADD THIS LINE
for (int i = 0; i < size; i++)
ptr[ i ] = 0; //initial values
}
There are some other problems with your code that are not the direct source of the crash but are worth noting:
Array::operator != is defined in terms of itself. It should be similar to operator==, or you can re-use it with
if( *this == right )
return false;
return true;
Array::operator [] should probably throw an exception if the index is out of bounds. Currently it just returns garbage memory.
Inside Array::Array (int size ) the assignment to size assigns to the parameter, not to the member. Change the first line to:
this->size = (size > 0 ? size : 10);
operator<< and operator>> should return output and input, respectively.
ostream & operator << ( ostream & output, const Array & array){
for (int i = 0; i < array.size; i++)
output << array.ptr[i] ;
return output;
}
Also, you have en error in your implementation of operator != at line:
if ( *this != right ) - recursive definition, so, stack overflow.
You have 2 errors in your default constructor:
1) You do not allocate memory for ptr and you try to initialize it, this is certainly an error and cause an undefined behavior, so if you have some invalid value in ptr you may get a segmentation fault or worse you may overwrite value of some of your internal variables!
2) Name of variable of the default constructor is size and size = (size > 0 ? size : 10); change value of local variable size not the size member of your class, and because of that your size member will remain uninitialized and any use of that is illegal and you may still get exceptions like segmentation fault(for example size may be 7476327436 that certainly is far beyond end of your array.
and beside that you have 1 error in your operator !=, since you have if ( *this != right ) and that will use operator != for comparison, and this is a recursive function in all cases and you will get a stack overflow exception, so if you want to check for exact pointers use if ( this != right ) instead of that.
I don't fully check your code first time that I see it, but you have some other errors in your code and I don't know how you even compile it, In multiple places you do not provide return value for your function. Please remember Never ignore compiler warnings there exist to help you correct your programming errors:
const int Array::operator [] (int subscript) const{
if(subscript >=0 && subscript < size)
return ptr[ subscript ];
// If not what should I do?? add a return value here, this is a warning
// since compiler think somehow you know that your code never reach here
// but do you really know??
return 0;
}
ostream & operator << ( ostream & output, const Array & array){
for (int i = 0; i < array.size; i++)
output << array.ptr[i] ;
// You say that your function return an ostream& but where is it??
// this is an error so compiler have nothing to return instead of you!
// And if your compiler does not generate an error possibly it return
// junk value that will cause an error!!
return output;
}
istream & operator >> ( istream & input, Array & array){
for (int i = 0; i < array.size; i++)
input >> array.ptr[i];
// again you forget to return an istream& and again this is an error
return input;
}
but beside that I see no error in your code and it should run with no error
Here are the full codes that I am using to implement this program. Everything seems to compile and run, but once it runs my find method, the program seems to stop and does not execute the last line stating the matching substring within the main.cpp file. Any help is definitely appreciated!
.h file:
#include <iostream>
using namespace std;
class MyString
{
public:
MyString();
MyString(const char *message);
MyString(const MyString &source);
~MyString();
const void Print() const;
const int Length() const;
MyString& operator()(const int index, const char b);
char& operator()(const int i);
MyString& operator=(const MyString& rhs);
bool operator==(const MyString& other) const;
bool operator!=(const MyString& other) const;
const MyString operator+(const MyString& rhs) const;
MyString& operator+=(const MyString& rhs);
friend ostream& operator<<(ostream& output, const MyString& rhs);
const int Find(const MyString& other);
MyString Substring(int start, int length);
private:
char *String;
int Size;
};
istream& operator>>(istream& input, MyString& rhs);
.cpp file:
#include <iostream>
#include <cstdlib>
#include "MyString.h"
using namespace std;
//default constructor that sets the initial string to the value "Hello World"
MyString::MyString()
{
char temp[] = "Hello World";
int counter(0);
while(temp[counter] != '\0')
{
counter++;
}
Size = counter;
String = new char [Size];
for(int i=0; i < Size; i++)
String[i] = temp[i];
}
//alternate constructor that allows for setting of the inital value of the string
MyString::MyString(const char *message)
{
int counter(0);
while(message[counter] != '\0')
{
counter++;
}
Size = counter;
String = new char [Size];
for(int i=0; i < Size; i++)
String[i] = message[i];
}
//copy constructor
MyString::MyString(const MyString &source)
{
int counter(0);
while(source.String[counter] != '\0')
{
counter++;
}
Size = counter;
String = new char[Size];
for(int i = 0; i < Size; i++)
String[i] = source.String[i];
}
//Deconstructor
MyString::~MyString()
{
delete [] String;
}
//Length() method that reports the length of the string
const int MyString::Length() const
{
int counter(0);
while(String[counter] != '\0')
{
counter ++;
}
return (counter);
}
/*Parenthesis operator should be overloaded to replace the Set and Get functions of your previous assignment. Note that both instances should issue exit(1) upon violation of the string array bounaries.
*/
MyString& MyString::operator()(const int index, const char b)
{
if(String[index] == '\0')
{
exit(1);
}
else
{
String[index] = b;
}
}
char& MyString::operator()(const int i)
{
if(String[i] == '\0')
{
exit(1);
}
else
{
return String[i];
}
}
/*Assignment operator (=) which will copy the source string into the destination string. Note that size of the destination needs to be adjusted to be the same as the source.
*/
MyString& MyString::operator=(const MyString& rhs)
{
if(this != &rhs)
{
delete [] String;
String = new char[rhs.Size];
Size = rhs.Size;
for(int i = 0; i < rhs.Size+1 ; i++)
{
String[i] = rhs.String[i];
}
}
return *this;
}
/*Logical comparison operator (==) that returns true iff the two strings are identical in size and contents.
*/
bool MyString::operator==(const MyString& other)const
{
if(other.Size == this->Size)
{
for(int i = 0; i < this->Size+1; i++)
{
if(&other == this)
return true;
}
}
else
return false;
}
//Negated logical comparison operator (!=) that returns boolean negation of 2
bool MyString::operator!=(const MyString& other) const
{
return !(*this == other);
}
//Addition operator (+) that concatenates two strings
const MyString MyString::operator+(const MyString& rhs) const
{
char* tmp = new char[Size + rhs.Size +1];
for(int i = 0; i < Size; i++)
{
tmp[i] = String[i];
}
for(int i = 0; i < rhs.Size+1; i++)
{
tmp[i+Size] = rhs.String[i];
}
MyString result;
delete [] result.String;
result.String = tmp;
result.Size = Size+rhs.Size;
return result;
}
/*Addition/Assigment operator (+=) used in the following fashion: String1 += String2 to operate as String1 = String1 + String2
*/
MyString& MyString::operator+=(const MyString& rhs)
{
char* tmp = new char[Size + rhs.Size + 1];
for(int i = 0; i < Size; i++)
{
tmp[i] = String[i];
} for(int i = 0; i < rhs.Size+1; i++)
{
tmp[i+Size] = rhs.String[i];
}
delete [] String;
String = tmp;
Size += rhs.Size;
return *this;
}
istream& operator>>(istream& input, MyString& rhs)
{
char* t;
int size(256);
t = new char[size];
input.getline(t,size);
rhs = MyString(t);
delete [] t;
return input;
}
ostream& operator<<(ostream& output, const MyString& rhs)
{
if(rhs.String != '\0')
{
output << rhs.String;
}
else
{
output<<"No String to output\n";
}
return output;
}
const int MyString::Find(const MyString& other)
{
int nfound = -1;
if(other.Size > Size)
{
return nfound;
}
int i = 0, j = 0;
for(i = 0; i < Size; i++)
{
for(j = 0; j < other.Size; j++)
{
if( ((i+j) >= Size) || (String[i+j] != other.String[j]) )
{
break;
}
}
if(j == other.Size)
{
return i;
}
}
return nfound;
}
/*MyString::Substring(start, length). This method returns a substring of the original string that contains the same characters as the original string starting at location start and is as long as length.
*/
MyString MyString::Substring(int start, int length)
{
char* sub;
sub = new char[length+1];
while(start != '\0')
{
for(int i = start; i < length+1; i++)
{
sub[i] = String[i];
}
}
return MyString(sub);
}
//Print() method that prints the string
const void MyString::Print() const
{
for(int i=0; i < Size; i++)
{
cout<<String[i];
}
cout<<endl;
}
main.cpp file:
#include <cstdlib>
#include <iostream>
#include "MyString.h"
using namespace std;
/*
*
*/
int main (int argc, char **argv)
{
MyString String1; // String1 must be defined within the scope
const MyString ConstString("Target string"); //Test of alternate constructor
MyString SearchString; //Test of default constructor that should set "Hello World". W/o ()
MyString TargetString (String1); //Test of copy constructor
cout << "Please enter two strings. ";
cout << "Each string needs to be shorter than 256 characters or terminated by /\n." << endl;
cout << "The first string will be searched to see whether it contains exactly the second string. " << endl;
cin >> SearchString >> TargetString; // Test of cascaded string-extraction operator
if(SearchString.Find(TargetString) == -1) {
cout << TargetString << " is not in " << SearchString << endl;
}
else {
cout << TargetString << " is in " << SearchString << endl;
cout << "Details of the hit: " << endl;
cout << "Starting poisition of the hit: " << SearchString.Find(TargetString) << endl;
cout << "The matching substring is: " << SearchString.Substring(SearchString.Find(TargetString), TargetString.Length());
}
return 0;
}
It appears the inner loop's invariant is that j is between 0 and end-2 inclusive. Hence j will NEVER equal end (the "matching" condition).
Looks like you have a problem with your found logic.
Your for loop is defined as for(int j = 0; j < end -1; j++)
but then you test for if(j == end)
j can never be equal to end in this for loop. Consider what you're actually trying to test for in your if statement.
I think you need to declare i and j outside the loops.
I think you meant j < end and not j < end - 1
I think you need to if((i+j>=end1) || String[i+j] != other.String[j]) and not just if(String[i+j] != other.String[j])
and if(j == end) needs to be outside the inner loop.
Here is a similar implementation.
#include <string>
#include <iostream>
using namespace std;
class MyString
{
private:
string String;
unsigned int Size;
public:
MyString() {
this->String = "";
this->Size = 0;
}
MyString(string initial_value) {
this->String = initial_value;
this->Size = initial_value.length();
}
const int Find(const MyString& other);
};
const int MyString::Find(const MyString& other)
{
if (other.Size > Size)
return -1; // if the substring is greater then us, there's no way we can have it as a substring
int i = 0, j = 0;
for (i = 0; i < Size; i++)
{
for (j = 0; j < other.Size; j++)
if ( ((i + j) >= Size) || (String[i + j] != other.String[j]) ) // if they don't match, offset exceeded Size, break
break ;
if (j == other.Size) // We went through the entire substring, didn't hit break so j == Other.size
return i; // return index
}
return -1; // if we never return anything means, we didn't find it, so return -1
}
int main()
{
string temp1, temp2;
getline(std::cin, temp1, '\n');
getline(std::cin, temp2, '\n');
MyString main_string(temp1), sub_string(temp2);
cout << main_string.Find(sub_string) << endl;
return 0;
}
MyString MyString::Substring(int start, int length)
{
char* sub = new char[length + 2]; // 2 byte buffer to be safe
int i = 0;
for (i = 0; i < length; i++)
sub[i] = String[start + i];
sub[i] = '\0'; // always null terminated to be safe!
return MyString(sub);
}
if theres any bugs or issues, I apologize, haven't tested it.
Along with what everyone else said, in your Substring method you have the following bit of code:
while(start != '\0')
{
for(int i = start; i < length+1; i++)
{
sub[i] = String[i];
}
}
Take a moment to go over the logic of the while loop and ask yourself "what am I trying to achieve here, and what does this code actually do?"
This is my first time with much of this code. With this instancepool.h file below I get errors saying I can't use vector (line 14) or have instance& as a return type (line 20). It seems it can't use the instance objects despite the fact that I have included them.
#ifndef _INSTANCEPOOL_H
#define _INSTANCEPOOL_H
#include "instance.h"
#include <iostream>
#include <string>
#include <vector>
#include <stdlib.h>
using namespace std;
class InstancePool
{
private:
unsigned instances;//total number of instance objects
vector<instance> ipp;//the collection of instance objects, held in a vector
public:
InstancePool();//Default constructor. Creates an InstancePool object that contains no Instance objects
InstancePool(const InstancePool& original);//Copy constructor. After copying, changes to original should not affect the copy that was created.
~InstancePool();//Destructor
unsigned getNumberOfInstances() const;//Returns the number of Instance objects the the InstancePool contains.
const instance& operator[](unsigned index) const;
InstancePool& operator=(const InstancePool& right);//Overloading the assignment operator for InstancePool.
friend istream& operator>>(istream& in, InstancePool& ip);//Overloading of the >> operator.
friend ostream& operator<<(ostream& out, const InstancePool& ip);//Overloading of the << operator.
};
#endif
Here is the instance.h :
#ifndef _INSTANCE_H
#define _INSTANCE_H
///////////////////////////////#include "instancepool.h"
#include <iostream>
#include <string>
#include <stdlib.h>
using namespace std;
class Instance
{
private:
string filenamee;
bool categoryy;
unsigned featuress;
unsigned* featureIDD;
unsigned* frequencyy;
string* featuree;
public:
Instance (unsigned features = 0);//default constructor
unsigned getNumberOfFeatures() const; //Returns the number of the keywords that the calling Instance object can store.
Instance(const Instance& original);//Copy constructor. After copying, changes to the original should not affect the copy that was created.
~Instance() { delete []featureIDD; delete []frequencyy; delete []featuree;}//Destructor.
void setCategory(bool category){categoryy = category;}//Sets the category of the message. Spam messages are represented with true and and legit messages with false.//easy
bool getCategory() const;//Returns the category of the message.
void setFileName(const string& filename){filenamee = filename;}//Stores the name of the file (i.e. “spam/spamsga1.txt”, like in 1st assignment) in which the message was initially stored.//const string& trick?
string getFileName() const;//Returns the name of the file in which the message was initially stored.
void setFeature(unsigned i, const string& feature, unsigned featureID,unsigned frequency) {//i for array positions
featuree[i] = feature;
featureIDD[i] = featureID;
frequencyy[i] = frequency;
}
string getFeature(unsigned i) const;//Returns the keyword which is located in the ith position.//const string
unsigned getFeatureID(unsigned i) const;//Returns the code of the keyword which is located in the ith position.
unsigned getFrequency(unsigned i) const;//Returns the frequency
Instance& operator=(const Instance& right);//Overloading of the assignment operator for Instance.
friend ostream& operator<<(ostream& out, const Instance& inst);//Overloading of the << operator for Instance.
friend istream& operator>>(istream& in, Instance& inst);//Overloading of the >> operator for Instance.
};
#endif
Also, if it is helpful here is instance.cpp:
// Here we implement the functions of the class apart from the inline ones
#include "instance.h"
#include <iostream>
#include <string>
#include <stdlib.h>
using namespace std;
Instance::Instance(unsigned features) { //Constructor that can be used as the default constructor.
featuress = features;
if (features == 0)
return;
featuree = new string[featuress]; // Dynamic memory allocation.
featureIDD = new unsigned[featuress];
frequencyy = new unsigned[featuress];
return;
}
unsigned Instance::getNumberOfFeatures() const {//Returns the number of the keywords that the calling Instance object can store.
return featuress;}
Instance::Instance(const Instance& original) {//Copy constructor.
filenamee = original.filenamee;
categoryy = original.categoryy;
featuress = original.featuress;
featuree = new string[featuress];
for(unsigned i = 0; i < featuress; i++) {
featuree[i] = original.featuree[i];
}
featureIDD = new unsigned[featuress];
for(unsigned i = 0; i < featuress; i++) {
featureIDD[i] = original.featureIDD[i];
}
frequencyy = new unsigned[featuress];
for(unsigned i = 0; i < featuress; i++) {
frequencyy[i] = original.frequencyy[i];}
}
bool Instance::getCategory() const { //Returns the category of the message.
return categoryy;}
string Instance::getFileName() const { //Returns the name of the file in which the message was initially stored.
return filenamee;}
string Instance::getFeature(unsigned i) const { //Returns the keyword which is located in the ith position.//const string
return featuree[i];}
unsigned Instance::getFeatureID(unsigned i) const { //Returns the code of the keyword which is located in the ith position.
return featureIDD[i];}
unsigned Instance::getFrequency(unsigned i) const { //Returns the frequency
return frequencyy[i];}
Instance& Instance::operator=(const Instance& right) { //Overloading of the assignment operator for Instance.
if(this == &right) return *this;
delete []featureIDD;
delete []frequencyy;
delete []featuree;
filenamee = right.filenamee;
categoryy = right.categoryy;
featuress = right.featuress;
featureIDD = new unsigned[featuress];
frequencyy = new unsigned[featuress];
featuree = new string[featuress];
for(unsigned i = 0; i < featuress; i++) {
featureIDD[i] = right.featureIDD[i]; }
for(unsigned i = 0; i < featuress; i++) {
frequencyy[i] = right.frequencyy[i]; }
for(unsigned i = 0; i < featuress; i++) {
featuree[i] = right.featuree[i]; }
return *this;
}
ostream& operator<<(ostream& out, const Instance& inst) {//Overloading of the << operator for Instance.
out << endl << "<message file=" << '"' << inst.filenamee << '"' << " category=";
if (inst.categoryy == 0)
out << '"' << "legit" << '"';
else
out << '"' << "spam" << '"';
out << " features=" << '"' << inst.featuress << '"' << ">" <<endl;
for (int i = 0; i < inst.featuress; i++) {
out << "<feature id=" << '"' << inst.featureIDD[i] << '"' << " freq=" << '"' << inst.frequencyy[i] << '"' << "> " << inst.featuree[i] << " </feature>"<< endl;
}
out << "</message>" << endl;
return out;
}
istream& operator>>(istream& in, Instance& inst) { //Overloading of the >> operator for Instance.
string word;
string numbers = "";
string filenamee2 = "";
bool categoryy2 = 0;
unsigned featuress2;
string featuree2;
unsigned featureIDD2;
unsigned frequencyy2;
unsigned i;
unsigned y;
while(in >> word) {
if (word == "<message") {//if at beginning of message
in >> word;//grab filename word
for (y=6; word[y]!='"'; y++) {//pull out filename from between quotes
filenamee2 += word[y];}
in >> word;//grab category word
if (word[10] == 's')
categoryy2 = 1;
in >> word;//grab features word
for (y=10; word[y]!='"'; y++) {
numbers += word[y];}
featuress2 = atoi(numbers.c_str());//convert string of numbers to integer
Instance tempp2(featuress2);//make a temporary Instance object to hold values read in
tempp2.setFileName(filenamee2);//set temp object to filename read in
tempp2.setCategory(categoryy2);
for (i=0; i<featuress2; i++) {//loop reading in feature reports for message
in >> word >> word >> word;//skip two words
numbers = "";//reset numbers string
for (int y=4; word[y]!='"'; y++) {//grab feature ID
numbers += word[y];}
featureIDD2 = atoi(numbers.c_str());
in >> word;//
numbers = "";
for (int y=6; word[y]!='"'; y++) {//grab frequency
numbers += word[y];}
frequencyy2 = atoi(numbers.c_str());
in >> word;//grab actual feature string
featuree2 = word;
tempp2.setFeature(i, featuree2, featureIDD2, frequencyy2);
}//all done reading in and setting features
in >> word;//read in last part of message : </message>
inst = tempp2;//set inst (reference) to tempp2 (tempp2 will be destroyed at end of function call)
return in;
}
}
}
and instancepool.cpp:
// Here we implement the functions of the class apart from the inline ones
#include "instancepool.h"
#include "instance.h"
#include <iostream>
#include <string>
#include <vector>
#include <stdlib.h>
using namespace std;
InstancePool::InstancePool()//Default constructor. Creates an InstancePool object that contains no Instance objects
{
instances = 0;
ipp.clear();
}
InstancePool::~InstancePool() {
ipp.clear();}
InstancePool::InstancePool(const InstancePool& original) {//Copy constructor.
instances = original.instances;
for (int i = 0; i<instances; i++) {
ipp.push_back(original.ipp[i]);
}
}
unsigned InstancePool::getNumberOfInstances() const {//Returns the number of Instance objects the the InstancePool contains.
return instances;}
const Instance& InstancePool::operator[](unsigned index) const {//Overloading of the [] operator for InstancePool.
return ipp[index];}
InstancePool& InstancePool::operator=(const InstancePool& right) {//Overloading the assignment operator for InstancePool.
if(this == &right) return *this;
ipp.clear();
instances = right.instances;
for(unsigned i = 0; i < instances; i++) {
ipp.push_back(right.ipp[i]); }
return *this;
}
istream& operator>>(istream& in, InstancePool& ip) {//Overloading of the >> operator.
ip.ipp.clear();
string word;
string numbers;
int total;//int to hold total number of messages in collection
while(in >> word) {
if (word == "<messagecollection"){
in >> word;//reads in total number of all messages
for (int y=10; word[y]!='"'; y++){
numbers = "";
numbers += word[y];
}
total = atoi(numbers.c_str());
for (int x = 0; x<total; x++) {//do loop for each message in collection
in >> ip.ipp[x];//use instance friend function and [] operator to fill in values and create Instance objects and read them intot he vector
}
}
}
}
ostream& operator<<(ostream& out, const InstancePool& ip) {//Overloading of the << operator.
out << "<messagecollection messages=" << '"' << '>' << ip.instances << '"'<< endl << endl;
for (int z=0; z<ip.instances; z++) {
out << ip[z];}
out << endl<<"</messagecollection>\n";
}
This code is currently not writing to files correctly either at least, I'm sure it has many problems. I hope my posting of so much is not too much, and any help would be very much appreciated. Thanks!
You created an Instance type but are trying to use instance. Case matters.