I have to implement a string class with operators [], +, +=, <<, >>, ==, !=, <, <=, >, >= but I cannot use the string object or include the library for c++. Here is my code so far:
# define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstring>
#include<fstream>
using namespace std;
class MyString {
public:
MyString();
MyString(const char* chars);
int length() const;
char * getValue()const;
~MyString();
//copy constructor
MyString(const MyString&);
MyString& operator =(const MyString& s);
char& operator[](int i);
MyString& operator+=(const MyString& s);
friend ostream& operator<<(ostream&os, const MyString&s);
friend istream& operator >> (istream&is, MyString&s);
friend bool operator ==(const MyString& s1, const MyString& s2);
friend bool operator <(const MyString& s1, const MyString& s2);
private:
char* value;
int size;
};
MyString operator+(const MyString s1, const MyString& s2);
bool operator !=(const MyString& s1, const MyString& s2);
bool operator >=(const MyString& s1, const MyString& s2);
bool operator <=(const MyString& s1, const MyString& s2);
bool operator > (const MyString& s1, const MyString& s2);
//no arguments constructor
MyString::MyString() {
value = 0;
size = 0;
}
//constructor
MyString::MyString(const char* chars) {
size = strlen(chars);
value = new char[size + 1];
strcpy(value, chars);
}
int MyString::length()const {
return size;
}
char* MyString::getValue()const {
return value;
}
MyString::~MyString() {
delete[] value;
}
//copy constructor
MyString::MyString(const MyString& s) {
size = s.size;
value = new char[size + 1];
strcpy(value, s.value);
}
MyString& MyString::operator=(const MyString&s) {
if (s.value == 0) {
delete[] value;
value = 0;
size = 0;
return *this;
}
if (this != &s) {
delete[] value;
value = new char[s.size + 1];
strcpy(value, s.value);
size = s.size;
}
return *this;
}
char& MyString::operator[](int i) {
if (i < 0 || i >= size) {
cout << "Please enter correct value of index" << endl;
exit(0);
}
else {
return value[i];
}
return value[i];
}
MyString& MyString::operator+=(const MyString& s) {
char** temp = &value;
*temp = new char[size];
strcpy(value,*temp);
strcat(value, s.getValue());
if (size != 0) {
delete[] temp;
}
size = strlen(value);
return *this;
}
ostream& operator<<(ostream&out, const MyString&s) {
out << s.value;
return out;
}
istream& operator >> (istream&in, MyString&s) {
char*ptr = new char[100];
in >> ptr;
s = MyString(ptr);
delete ptr;
return in;
}
MyString operator+(const MyString s1, const MyString& s2) {
MyString answer;
answer += s1;
answer+= s2;
return answer;
}
bool operator ==(const MyString&s1, const MyString& s2) {
return(strcmp(s1.value, s2.value) == 0);
}
bool operator<(const MyString& s1, const MyString& s2) {
return (strcmp(s1.value, s2.value) < 0);
}
bool operator !=(const MyString& s1, const MyString& s2) {
return !(s1 == s2);
}
bool operator>(const MyString& s1, const MyString& s2) {
return !(s1 == s2) && !(s1 < s2);
}
bool operator<=(const MyString& s1, const MyString& s2) {
return s1 < s2 || s1 == s2;
}
bool operator >=(const MyString & s1, const MyString& s2) {
return !(s1 < s2);
}
void test_copy_and_destructor(MyString S) {
cout << "test: copy constructor and destructor calls: " << endl;
MyString temp = S;
cout << "temp inside function test_copy_and_destructor: " << temp << endl;
}
int main() {
MyString st1("abc abc");
MyString st2("9fgth");
cout << "Copy constructor , << operator" << endl;
MyString st3(st1);
cout << "st3: " << st3 << endl;
test_copy_and_destructor(st2);
MyString st4;
cout << "operator + " << endl;
st4 = st3 + st2;
cout << "st4: " << st4 << endl;
cout << "st1 + st2: " << (st1 + st2) << endl;
cout << "operators [ ] " << endl;
for (int i = 0; i < st2.length(); i++)
cout << st2[i] << " ";
cout << endl;
cout << "operators += , ==, != " << endl;
st2 += st1;
if (st3 == st1)
cout << "st3 and st1 are identical " << endl;
else cout << "st3 and st1 are not identical " << endl;
if (st2 != st1)
cout << "st2 and st1 are not identical " << endl;
else cout << "st2 and st1 are identical " << endl;
cout << "operators < , <=, >, >= " << endl;
if (st2 < st1)
cout << "st2 < st1 " << endl;
else cout << "st2 is not less than st1 " << endl;
if (st1 <= st2)
cout << "st1 <= st2 " << endl;
else cout << "st1 is not less than or equal to st2 " << endl;
if (st1 > st2)
cout << "st1 > st2 " << endl;
else cout << "not (st1 > st2) " << endl;
if (st1 >= st2)
cout << "st1 >= st2 " << endl;
else cout << "not (st1 >= st2) " << endl;
cout << "operator >> " << endl;
//Open the data file
ifstream input("A9_input.txt");
if (input.fail()) {
cout << "unable to open input file A9_input.txt, Exiting..... ";
return 0;
}
MyString temp1;
MyString temp2("aaa");
input >> temp1;
input >> temp2;
cout << "first element of input file: " << temp1 << endl;
cout << "second element of input file: " << temp2 << endl;
input.close();
cout << "MyString says farewell....." << endl;
return 0;
}
After reaching operator + my code reaches break point and crashes. I could tell something is wrong with my += operator code as + is using += code. Can someone help me figure out what is wrong with my += code?
The problem is that you are using strcpy() and strcat() to populate the old allocation, which is not large enough. Therefore, the strcat() invocation attempts to write past the end of this allocation and the result is undefined behavior, which is manifesting as a crash on your system.
char* temp = value;
temp = new char[strlen(value) + s.length() +1];
strcpy(value, temp); // <-- should be copying the other way
strcat(value, s.getValue()); // <-- should be copying into temp, not value
//if (size != 0) {
// delete[]temp;
//}
size = strlen(value);
return *this;
Rewritten to solve this problem, as well as release the original string, and check for value == nullptr:
char *oldString = value == nullptr ? "" : value;
int newSize = strlen(oldString) + s.length();
char *newString = new char[newSize + 1];
strcpy(newString, oldString);
strcat(newString, s.getValue());
delete [] value;
value = newString;
size = newSize;
return *this;
your problem is that you are trying to change what you pointer points to be using a pointer and not pointer to pointer.
when you are using the new operator your pointer simply gets new adress at the heap and than the assignment you did before(char * temp = value) is irelvent.
beacuse temp gets the adress pointed by value and not value adress, than any modification on temp adress will not effect value.
what you really wanna do is :
char * temp = new char[size]
//modify temp(add the strings)
//change value to hold the modified string
value = temp;
you can also do that :
char ** temp = &value;//now temp holds value's address.
*temp = new char[size];
//modify temp will modify value, but you need to derefrence before each use.
update
you are trying to copy a larger string temp into value which casues overflow (its a good practise to use strcpy_s/strcat_s beacuse they cannot copy more than the length you give them, that should be the size of dest).
link to strcpy/strcpy_s doc :
http://en.cppreference.com/w/c/string/byte/strcpy
http://en.cppreference.com/w/c/string/byte/strcat
what you should do is :
char * temp = new char[size];
//strcpy_s(dest, length of dest, src)
strcpy_s(temp , sizeof temp, value);//copy value to temp
strcat_s(temp, sizeof temp, s.getValue());
if(value != nullptr)
delete [] value;
value = temp;
Related
As you can see that operator+ is returning an object of class type MyString.
The assignment operator is not accepting this object in Visual Studio 2019 and giving an error of E0349 that no operands matches. But, in Visual Studio 2012, it is running fine.
Code:
#include <iostream>
using namespace std;
class MyString {
private:
char* str;
int length;
public:
MyString();
MyString(const MyString&);
bool operator!();
friend ostream& operator<<(ostream&, MyString&);
friend istream& operator>>(istream&, MyString&);
MyString& operator=(const MyString&);
MyString operator+(MyString&);
MyString& operator-(MyString);
MyString& operator ++ ();
MyString& operator -- ();
bool operator<(MyString&);
char& operator[](int);
char* operator()(int, int);
void my_strcpy(char*&, char*);
int my_strlen(char*);
};
MyString::MyString() {
str = new char[1];
str[0] = '\0';
length = 0;
}
MyString::MyString(const MyString& obj) {
int size = my_strlen(obj.str) + 1;
str = new char[size];
cout << "copy constructor called\n";
my_strcpy(str, obj.str);
}
bool MyString::operator!() {
if (str[0] == '\0')
return true;
return false;
}
ostream& operator<<(ostream& output, MyString& obj) {
output << obj.str;
return output;
}
istream& operator>>(istream& input, MyString& obj) {
delete obj.str;
obj.str = new char[50];
input.getline(obj.str, 50);
return input;
}
MyString& MyString::operator=(const MyString& obj) {
if (this != &obj) {
my_strcpy(str, obj.str);
}
return *this;
}
char& MyString::operator[](int index) {
if (index >= 0 && index < my_strlen(str))
return str[index];
else {
clog << "Index is out of range\n", exit(1);
}
}
void MyString::my_strcpy(char*& destination, char* source) {
int len_des = my_strlen(destination);
int len_source = my_strlen(source);
delete[]destination;
destination = new char[len_source + 1];
int index = 0;
while (source[index] != '\0')
destination[index] = source[index++];
destination[index] = '\0';
}
MyString MyString::operator+(MyString& obj) {
MyString newObj;
int size = my_strlen(str) + my_strlen(obj.str) + 1;
newObj.str = new char[size];
char* temp = newObj.str;
int index_str = 0;
while (str[index_str] != '\0')
*newObj.str++ = str[index_str++];
index_str = 0;
while (obj.str[index_str] != '\0')
*newObj.str++ = obj.str[index_str++];
*newObj.str = '\0';
newObj.str = temp;
cout << newObj.str << endl;
return newObj;
}
MyString& MyString::operator ++ () {
int size = my_strlen(str) + 2;
char* new_str = new char[size];
char* temp = new_str;
int index = 0;
while (str[index] != '\0')
*new_str++ = str[index++];
*new_str++ = 'a';
*new_str = '\0';
delete[]str;
str = temp;
return *this;
}
MyString& MyString::operator -- () {
int size = my_strlen(str);
char* new_str = new char[size];
char* temp = new_str;
int index = 0;
while (index < size - 1)
*new_str++ = str[index++];
*new_str = '\0';
delete[]str;
str = temp;
return *this;
}
MyString& MyString::operator-(MyString obj) {
bool find = true;
int obj_str_len = my_strlen(obj.str);
int str_len = my_strlen(str);
int start_ind = 0, end_ind = 0;
if (str_len > obj_str_len) {
for (int i = 0; str[i] != '\0'; i++) {
if (str[i] == obj.str[0]) {
start_ind = i;
find = true;
for (int j = i + 1, k = 1; obj.str[k] != '\0' && find; j++, k++) {
if (str[j] != obj.str[k]) {
find = false;
}
end_ind = j + 1;
}
if (find)
break;
}
}
if (find) {
while (str[end_ind] != '\0') {
str[start_ind++] = str[end_ind++];
}
str[str_len - (end_ind - start_ind)] = '\0';
}
}
return *this;
}
bool MyString::operator<(MyString& obj) {
if (my_strlen(str) < my_strlen(obj.str))
return true;
else if (my_strlen(str) > my_strlen(obj.str))
return false;
for (int i = 0; str[i] != '\0'; i++) {
if (str[i] < obj.str[i])
return true;
else if (str[i] > obj.str[i]) {
return false;
}
}
return false;
}
char* MyString::operator()(int starting_index, int ending_index) {
int new_str_size = ending_index - starting_index + 2;
char* new_str = new char[new_str_size];
char* temp = new_str;
int index = 0;
int str_len = my_strlen(str);
while (starting_index < str_len && index < 10)
*new_str++ = str[starting_index++], index++;
*new_str = '\0';
return temp;
}
int MyString::my_strlen(char* str) {
int size = 0;
while (str[size] != '\0')
size++;
return size;
}
int main() {
MyString str1, str2, str3, str4, str6;
if (!str1)
{
cout << "String 1 is Empty.\n";
cout << "Str 1 = " << str1 << endl << endl << endl;
}
cout << "Enter String 1:\t";
cin >> str1;
cout << "Enter String 2:\t";
cin >> str2;
cout << "\n\n\nUser Entered:\n";
cout << "String 1 = " << str1 << endl;
cout << "String 2 = " << str2 << endl << endl << endl;
cout << "Before str1 = str1; str1 = " << str1 << endl;
str1 = str1;
cout << "After str1 = str1, str1 = " << str1 << endl << endl << endl;
cout << "Before str4 = str3 = str1+str2\n";
cout << "str1 = " << str1 << endl;
cout << "str2 = " << str2 << endl;
cout << "str3 = " << str3 << endl;
cout << "str4 = " << str4 << endl;
str4 = str3 = str1 + str2;
cout << "\n\n\nAfter str4 = str3 = str1+str2\n";
cout << "str1 = " << str1 << endl;
cout << "str2 = " << str2 << endl;
cout << "str3 = " << str3 << endl;
cout << "str4 = " << str4 << endl;
cout << "\n\n\nEnter String 3:\t";
cin >> str3;
cout << "\n\n\nEnter String 4:\t";
cin >> str4;
cout << "\n\n\nstr3 = " << str3 << endl;
cout << "str4 = " << str4 << endl;
if (str3 < str4)
cout << "String 3 is Less than String 4.\n";
else
cout << "String 3 is NOT Less than String 4.\n";
MyString str5 = str1 + str2;;
cout << "\n\n\nStr5:\t" << str5 << endl;
cout << "Str5[7]:\t" << str5[7] << endl;
str5[7] = '$';
cout << "\n\nStr5:\t" << str5 << endl;
cout << "\n\n\nstr5(5, 10):\t" << str5(5, 10) << endl;
cout << "\n\n ++Str3 :\t" << ++str3 << endl;
str5 = str4 - str3;
cout << "\n\n Str4 - Str3 :\t" << str5 << endl;
cout << "\n\n --Str3 :\t" << --str3 << endl;
cout << "\n\n --Str3 :\t" << --str3 << endl;
str5 = str4 - str3;
cout << "\n\n Str4 - str3 :\t" << str5 << endl;
return 0;
}
Problem:
The lines str3 = str1 + str2; and MyString str4 = str1 + str2; will give an error that no operands matches. This error starts from Visual Studio 2019 and onwards versions.
But, when I use const in both copy constructor and assignment operator (such as MyString(const MyString&); MyString& operator=(const MyString&);), both lines (str3 = str1 + str2; MyString str4 = str1 + str2;) looks fine to compiler.
But if I Write str3 = str1; and MyString str4 = str2; now there is no need to write const in the copy constructor and assignment operator.
Why is the compiler showing this behaviour.?
The assignment operator that you have defined (i.e., taking a non-const reference) requires that its argument be (or be convertible to) an lvalue reference because it is non-const.
However, the right-hand side of the assignment operator in the expression, str3 = str1 + str2 is the result of the addition, which is a temporary value and, as such, is a prvalue that cannot be (implicitly) converted to an lvalue.
Adding the const qualifier to the parameter allows that operator to accept rvalue references (because you are explicitly stating that the referred-to value will not be modified).
This is why, generally, copy constructors and copy assignment operators are defined with const &T parameters (though they don't have to be).
For a fuller discussion of the differences between lvalues and rvalues, see here: Exact difference between rvalue and lvalue.
On why/how this "binding of a temporary to an lvalue reference" was allowed in the earlier version(s) of Visual Studio, see: Non-const reference bound to temporary, Visual Studio bug?.
My college teacher assigned a homework about simulating string, and here's one selection of my code:
// MyString.h
#include <iostream>
#include <cstdio>
#include <string.h>
using std::ostream;
using std::istream;
class MyString{
protected:
char *_data; // data index
int _len; // index size
public:
/////////////////////////////////////////////////////////
char* data()const{
return _data;
}
/////////////////////////////////////////////////////////
MyString& operator=(const char* &rhs){
_data = const_cast<char*>(rhs);
_len = strlen(_data)
}
MyString& operator=(const MyString& rhs){
(*this) = rhs._data;
}
/////////////////////////////////////////////////////////
friend ostream &operator<<( ostream&, const MyString & );
friend istream &operator>>( istream&, MyString & );
};
ostream& operator <<(ostream& os, const MyString& str){
os << str.data();
return os;
}
istream& operator >>(istream& is, MyString& str){
char buffer[0xff];
is.getline(buffer, 0xff);
str = buffer;
return is;
}
I totally have no idea why operator = causes infinite loop here...please help...thanks in advance
Edit:
There goes my buggy code. I have edited where cause the causes infinite loop issue but the rest of them maybe still bugged, I'll try to fix them soon, thank you all!
MyString.h
#include <iostream>
#include <cstdio>
#include <string.h>
using std::ostream;
using std::istream;
class MyString{
protected:
char *_data; // data index
int _len; // index length
static int _total_MyString; // number of instance created
public:
/////////////////////////////////////////////////////////
// basic initialization
MyString(){
_data = NULL;
_len = 0;
_total_MyString++;
}
// init with defined string
MyString(const char *s):MyString(){
(*this)= s;
}
// init with same class data
MyString(const MyString & s):MyString(s._data){}
// finalizer
~MyString(){
_total_MyString--;
delete _data;
}
/////////////////////////////////////////////////////////
static int total_MyString(){
return _total_MyString;
}
/////////////////////////////////////////////////////////
char* data()const{
return _data;
}
/////////////////////////////////////////////////////////
MyString& operator=(const char* &rhs){
char* temp = const_cast<char*>(rhs);
_len = strlen(temp);
_data = new char [_len + 1];
strcpy(_data, rhs);
return (*this);
}
MyString& operator=(const MyString& rhs){
const char* temp = rhs._data;
return (*this = temp);
}
/////////////////////////////////////////////////////////
MyString& operator+=(const char* &rhs){
_len += strlen(rhs);
strcat(_data, rhs);
return *this;
}
MyString& operator+=(const MyString& rhs){
const char* temp = rhs._data;
(*this) += temp;
return *this;
}
/////////////////////////////////////////////////////////
inline char & operator[](const int pos){
return _data[pos];
}
/////////////////////////////////////////////////////////
unsigned length() const{
return _len;
}
/////////////////////////////////////////////////////////
friend ostream &operator<<( ostream&, const MyString & );
friend istream &operator>>( istream&, MyString & );
/////////////////////////////////////////////////////////
bool operator==(const MyString& str){
return !strcmp(_data, str._data);
}
friend bool operator!=(const MyString& str, const MyString& str2){
return strcmp(str._data, str2._data) != 0;
}
};
ostream& operator <<(ostream& os, const MyString& str){
os << str.data();
return os;
}
istream& operator >>(istream& is, MyString& str){
char buffer[0xff];
is.getline(buffer, 0xff);
const char* temp = &buffer[0];
str = temp;
return is;
}
MyString.cpp
#include "MyString.h"
using namespace std ;
int MyString::_total_MyString = 0;
int main()
{
MyString Dstr1("String"), Dstr2("Test String 2"), Dstr3(Dstr1);
cout << "Dstr1 is: " << Dstr1 << endl;
cout << "Dstr2 is: " << Dstr2 << endl;
cout << "Dstr3 is: " << Dstr3 << endl;
cout << "Total MyString_Derived is: " << MyString_Derived::total_MyString() << endl;
/////////////////////////////////////////////////////////
cout << "Give one word "<< endl;
cin >> Dstr1;
cout << "Dstr1 is: " << Dstr1 << endl;
/////////////////////////////////////////////////////////
Dstr2 = Dstr1;
cout << "Dstr2 is: " << Dstr2 << endl;
return 0;
/////////////////////////////////////////////////////////
Dstr3 += Dstr1;
cout << "Dstr3 is: " << Dstr3 << endl;
/////////////////////////////////////////////////////////
cout << "Dstr3 is: ";
for(int i = 0; i<Dstr3.length(); i++)
cout << Dstr3[i];
cout << endl;
for(int i = 0; i<Dstr3.length(); i++)
Dstr3[i] = 'a' + i;
cout << "Dstr3 is: " << Dstr3 << endl;
/////////////////////////////////////////////////////////
cout << "Dstr1 is: " << Dstr1 << endl;
cout << "Dstr2 is: " << Dstr2 << endl;
cout << "Dstr3 is: " << Dstr3 << endl;
cout << "Compare Dstr1 == Dstr2 is: " << (Dstr1 == Dstr2) << endl;
cout << "Compare Dstr1 == Dstr3 is: " << (Dstr1 == Dstr3) << endl;
cout << "Compare Dstr1 != Dstr2 is: " << (Dstr1 != Dstr2) << endl;
cout << "Compare Dstr1 != Dstr3 is: " << (Dstr1 != Dstr3) << endl;
}
Consider the operator>>
istream& operator >>(istream& is, MyString& str){
char buffer[0xff];
is.getline(buffer, 0xff);
const char* temp = &buffer[0];
str = temp;
return is;
}
buffer is a local variable. you create a pointer to this local array and assing it to str. That calls
MyString& operator=(const char* &rhs){
_data = const_cast<char*>(rhs);
_len = strlen(_data);
return (*this);
}
Which is interesting: as a parameter you give the reference to a pointer, which is const, and you then strip the const-ness from it and assign it to _data.
Now char* _data is pointing to the local array buffer. What happens at the end of the function, when the local array is destroyed? The pointer becomes invalid.
You need to implement a deep copy.
After a fairly lengthy session with my instructor and not being able to come up with a concrete solution, I figured I'd come back here for help on a homework assignment. We're supposed to translate the string class to our own version that uses a linked list with a few basic functions. Everything seems to work so far except the + operator function (adding two strings together).
string operator +(string& s1, string& s2);
What's strange is that it appears to return the correct result when tested, only to cause a crash with a debug assertion failure. I cannot tell why this would be, since the definition:
string operator +(string& s1, string& s2)
{
s1 += s2;
return s1;
}
relies on +=, but that operator works with no issue with the test code in main(). Here's the full code:
HEADER FILE
#ifndef STRING2_H
#define STRING2_H
#include<iostream>
namespace string2
{
class string
{
private:
struct stringList
{
char character;
stringList* link;
};
stringList* headPtr;
public:
// CONSTRUCTORS AND DESTRUCTOR
string() { headPtr = NULL; };
string(const stringList* sourcePtr);
~string();
// CONSTANT MEMBER FUNCTIONS
char getChar(const size_t position) const;
size_t length() const;
char operator [ ] (size_t position) const;
// MODIFICATION MEMBER FUNCTIONS
void operator += (const string& addend);
void operator += (const char addend[]);
void operator += (char addend);
void operator =(const string& source);
// FRIEND FUNCTIONS
friend bool operator ==(const string& s1, const string& s2);
};
// NONMEMBER FUNCTIONS
string operator +(string& s1, string& s2);
std::ostream& operator <<(std::ostream& outs, const string& source);
}
#endif
IMPLEMENTATION
#include "String2.h"
#include <iostream>
namespace string2
{
string::string(const stringList* sourcePtr)
{
stringList* indexPtr;
if (sourcePtr == NULL)
{
headPtr = NULL;
return;
}
headPtr = new stringList;
indexPtr = headPtr;
indexPtr->character = sourcePtr->character;
indexPtr->link = sourcePtr->link;
sourcePtr = sourcePtr->link;
while (sourcePtr != NULL)
{
indexPtr = indexPtr->link;
indexPtr->character = sourcePtr->character;
indexPtr->link = sourcePtr->link;
sourcePtr = sourcePtr->link;
}
}
string::~string()
{
stringList *removePtr;;
while (headPtr != NULL)
{
removePtr = headPtr;
headPtr = headPtr->link;
delete removePtr;
}
}
char string::getChar(const size_t position) const
{
stringList *indexPtr = headPtr;
for (size_t i = 0; i < position - 1; i++)
indexPtr = indexPtr->link;
return indexPtr->character;
}
size_t string::length() const
{
size_t count = 0;
stringList* indexPtr = headPtr;
while (indexPtr != NULL)
{
count++;
indexPtr = indexPtr->link;
}
return count;
}
char string::operator [ ] (size_t position) const
{
stringList* indexPtr = headPtr;
for (size_t i = 0; i < position; i++)
indexPtr = indexPtr->link;
return indexPtr->character;
}
void string::operator += (const string& addend)
{
for (int index = 0; index < addend.length(); index++)
(*this) += addend[index];
}
void string::operator += (const char addend[])
{
if (addend[0] == NULL)
return;
for (int index = 0; index < (sizeof(addend) / sizeof(addend[0])); index++)
(*this) += addend[index];
}
void string::operator += (char addend)
{
stringList *indexPtr = headPtr;
if (headPtr == NULL)
{
headPtr = new stringList;
headPtr->character = addend;
headPtr->link = NULL;
return;
}
while (indexPtr->link != NULL)
indexPtr = indexPtr->link;
indexPtr->link = new stringList;
indexPtr->link->character = addend;
indexPtr->link->link = NULL;
}
void string::operator =(const string& source)
{
if (headPtr != NULL)
{
delete headPtr;
headPtr = NULL;
}
*this += source;
}
bool operator ==(const string& s1, const string& s2)
{
if (s1.length() != s2.length())
return false;
if (s1.headPtr == NULL && s2.headPtr == NULL)
return true;
for (int index = 0; index < s1.length(); index++)
{
if (s1.headPtr->character != s2.headPtr->character)
return false;
}
return true;
}
string operator +(string& s1, string& s2)
{
s1 += s2;
return s1;
}
std::ostream& operator <<(std::ostream& outs, const string& source)
{
for (int index = 0; index < source.length(); index++)
outs << source.getChar(index + 1);
return outs;
}
}
TEST CODE (final line is where it breaks)
#include "String2.h"
#include <iostream>
#include <fstream>
using namespace string2;
int main()
{
string test, test2, test3;
std::cout << "Current length: " << test.length() << std::endl;
char add[4] = { 'a', 'b', 'c', 'd' };
test += 's'; // testing third (lowest) += operator
std::cout << "First char in string: " << test.getChar(1) << std::endl;
test += 'd';
std::cout << "Current length: " << test.length() << std::endl;
std::cout << "Second char in string: " << test.getChar(2) << std::endl;
std::cout << "Second char in string testing [] operator: " << test[1] << std::endl;
std::cout << "Current string: " << test << std::endl;
test += add; // testing second (middle) += operator
std::cout << "Current length: " << test.length() << std::endl;
std::cout << "Current string: " << test << std::endl;
test2 += 'z';
test2 += 'y';
test += test2; // testing first (top) += operator
std::cout << "Current string: " << test << std::endl;
test = test2; // testing = operator
std::cout << "\nCurrent string: " << test << std::endl;
std::cout << "Compared to string: " << test2 << std::endl;
if (test == test2) // testing == operator
std::cout << "\nStrings are equal" << std::endl;
else
std::cout << "\nStrings are not equal." << std::endl;
test += 'f';
std::cout << "\nCurrent string: " << test << std::endl;
std::cout << "Compared to string: " << test2 << std::endl;
if (test == test2) // testing == operator
std::cout << "\nStrings are equal" << std::endl;
else
std::cout << "\nStrings are not equal." << std::endl;
std::cout << "\nTwo strings added together: " << test + test2 << std::endl; // testing + operator
}
Thank you for your help
Your problem is reproducible with this
int main()
{
string test, test2;
test += 'h';
std::cout << "\nTwo strings added together: " << test + test2 << std::endl; // testing + operator
}
string operator +(string& s1, string& s2)
{
s1 += s2;
return s1;
}
In operator +, return s1 returns a shallow copy of s1.So now the internal memory is being occupied by s1 as well as the returned string from operator+(). At the end of main() ~string() is triggered for s1 which frees the internal memory and then for the returned string from operator+() as well which tries to delete the already deleted memory causing double free.
You need a deep copy in operator+() or better, a rethink in design of operator+().
I just compiled this code, and it showed me this error:
Exception thrown at 0x0F640E09 (ucrtbased.dll) in ConsoleApplication5.exe: 0xC0000005: Access violation writing location 0x014C3000. I literally have no idea what this error means as I've just been using C++ for a couple months, and I've also tried looking on any other websites to look for help, but I didn't find any.
For this code, I'm only allowed to use the c-string functions and the <cstring> library. I cannot use the string object or include the library. I can also use helper methods/functions.
#include <iostream>
#include <cstring>
#include <fstream>
using namespace std;
class MyString {
public:
//default constructor
MyString();
MyString(char* chars);
//copy constructor
MyString(const MyString &);
int length() const;
//destructor
~MyString();
//operator overloads
char& operator[](int index);
friend MyString operator+(const MyString& newWord, const MyString& newWord2);
MyString& operator+=(const MyString& newWord);
friend ostream& operator<<(ostream& newWord, const MyString& newWord2);
friend istream& operator >> (istream& newWord, MyString& newWord2);
friend bool operator==(const MyString& newWord, const MyString& newWord2);
friend bool operator!=(const MyString& newWord, const MyString& newWord2);
friend bool operator<(const MyString& newWord, const MyString& newWord2);
friend bool operator<=(const MyString& newWord, const MyString& newWord2);
friend bool operator>(const MyString& newWord, const MyString& newWord2);
friend bool operator>=(const MyString& newWord, const MyString& newWord2);
private:
char* value;
int size;
};
//default constructor
MyString::MyString() {
value = 0;
size = 0;
}
//copy constructor
MyString::MyString(const MyString& newWord) {
//perform a deep copy to copy each of the value to a new memory
size = newWord.size;
value = new char[size];
for (int ii = 0; ii < size; ii++) {
value[ii] = newWord.value[ii];
}
}
//constructor with an argument
MyString::MyString(char* chars) {
size = strlen(chars);
value = new char[size];
for (int i = 0; i < size; i++) {
value[i] = chars[i];
}
}
//find length
int MyString::length() const {
return size;
}
//find the value of each index
char& MyString::operator[](int index) {
return value[index];
}
//operator + (concatenate)
MyString operator+(const MyString& newWord, const MyString& newWord2) {
MyString concatenated;
concatenated = strcat(newWord.value, newWord.value);
return concatenated;
}
//operator += (append)
MyString& MyString::operator+=(const MyString& newWord) {
char * newMemory = value;
value = new char[strlen(value) + newWord.length() + 1];
strcpy(value, newMemory);
strcat(value, newWord.value);
if (size != 0)
{
delete[] newMemory;
}
size = strlen(value);
return *this;
}
//ostream operator
ostream& operator<<(ostream& newWord, const MyString& newWord2) {
newWord << newWord2.value;
return newWord;
}
//istream operator
istream& operator >> (istream& newWord, MyString& newWord2) {
const int MAX = 100;
char* ptr = new char[MAX];
newWord >> ptr;
newWord2 = MyString(ptr);
delete ptr;
return newWord;
}
//all boolean operators
bool operator==(const MyString& newWord, const MyString& newWord2) {
if (newWord.value == newWord2.value) {
return true;
}
else {
return false;
}
}
bool operator!=(const MyString& newWord, const MyString& newWord2) {
if (newWord.value != newWord2.value) {
return true;
}
else {
return false;
}
}
bool operator<(const MyString& newWord, const MyString& newWord2) {
if (newWord.value < newWord2.value) {
return true;
}
else {
return false;
}
}
bool operator<=(const MyString& newWord, const MyString& newWord2) {
if (newWord.value <= newWord2.value) {
return true;
}
else {
return false;
}
}
bool operator>(const MyString& newWord, const MyString& newWord2) {
if (newWord.value > newWord2.value) {
return true;
}
else {
return false;
}
}
bool operator>=(const MyString& newWord, const MyString& newWord2) {
if (newWord.value >= newWord2.value) {
return true;
}
else {
return false;
}
}
//destructor to release memory
MyString::~MyString() {
delete[] value;
}
void test_copy_and_destructor(MyString S) {
cout << "test: copy constructor and destructor calls: " << endl;
MyString temp = S;
cout << "temp inside function test_copy_and_destructor: " << temp << endl;
}
int main() {
MyString st1("abc abc");
MyString st2("9fgth");
cout << "Copy constructor , << operator" << endl;
MyString st3(st1);
cout << "st3: " << st3 << endl;
test_copy_and_destructor(st2);
MyString st4;
cout << "operator + " << endl;
st4 = st3 + st2;
cout << "st4: " << st4 << endl;
cout << "st1 + st2: " << (st1 + st2) << endl;
cout << "operators [ ] " << endl;
for (int i = 0; i < st2.length(); i++)
cout << st2[i] << " ";
cout << endl;
cout << "operators += , ==, != " << endl;
st2 += st1;
if (st3 == st1)
cout << "st3 and st1 are identical " << endl;
else cout << "st3 and st1 are not identical " << endl;
if (st2 != st1)
cout << "st2 and st1 are not identical " << endl;
else cout << "st2 and st1 are identical " << endl;
cout << "operators < , <=, >, >= " << endl;
if (st2 < st1)
cout << "st2 < st1 " << endl;
else cout << "st2 is not less than st1 " << endl;
if (st1 <= st2)
cout << "st1 <= st2 " << endl;
else cout << "st1 is not less than or equal to st2 " << endl;
if (st1 > st2)
cout << "st1 > st2 " << endl;
else cout << "not (st1 > st2) " << endl;
if (st1 >= st2)
cout << "st1 >= st2 " << endl;
else cout << "not (st1 >= st2) " << endl;
cout << "operator >> " << endl;
//Open the data file
ifstream input("A9_input.txt");
if (input.fail()) {
cout << "unable to open input file A9_input.txt, Exiting..... ";
system("pause");
return 0;
}
MyString temp1;
MyString temp2("aaa");
input >> temp1;
input >> temp2;
cout << "first element of input file: " << temp1 << endl;
cout << "second element of input file: " << temp2 << endl;
input.close();
cout << "MyString says farewell....." << endl;
system("pause");
return 0;
}
You error most likely should be on following code:
st4 = st3 + st2; # note that you initialize st4 with type MyString but inside operator+ you assign a char[] to MyString. Just letting you know since you dont have a operator= overloaded function
calls:
MyString operator+(const MyString& newWord, const MyString& newWord2) {
MyString concatenated;
concatenated = strcat(newWord.value, newWord.value); # you have an mistake here, second parameter should be newWord2.value
return concatenated;
}
You are making an assumption that your newWord.value holds enough space to hold newWord.value and newWord2.value. but it is not you inisialized it on your constructor to be newWord.value so you are basically writng in to a region that violate memory access. What you want to do is make newWord big enough to hold both strings.
reference to strcat : http://www.cplusplus.com/reference/cstring/strcat/
Your operator + is writing past the allocated memory (buffer overflow). You can easily change it to the very simple:
MyString operator +(const MyString& newWord, const MyString& newWord2) {
MyString concatenated;
return concatenated += newWord2;
}
then it even doesn't need to be a friend of the class.
Also the operator += is wrong, because your strings are not created NULL terminated initially, so in the fact you shouldn't use strlen(), strcpy() and strcat() at all, because you are then concatenating arbitrary memory together (reading past allocated memory can also segfault). So you should make your mind whether you want the strings NULL terminated (and used as such) or not.
The operator += is also not defined very effectively anyway, more clean and effective could be for example:
MyString& MyString::operator+=(const MyString& newWord) {
size_t newSize = size + newWord.size;
char * newValue = new char[newSize /* + 1 */]; // not null terminated, but cannot be printed easily then
// or make the string initially null terminated and then allocate +1 and null terminate then
memcpy(newValue, value, size);
memcpy(newValue + size, newWord.value, newWord.size /* + 1 */);
delete[] value;
value = newValue;
size = newSize;
return *this;
}
See? No use of strlen() which traverses the strings (not null terminated in your case), because you know the size of the string anyway. You can also use memcpy() in the constructors instead of the for (int i = 0; i < size; i++) loops. Or strcpy() and make the string null-terminated (but even then memcpy() can be used as it is faster - does not test for the '\0' on every character copied).
The operators ==, != do not seem very correct either, as only the pointers are compared (so the string will only ever be equal to itself, but not to other string with the same characters stored in value).
Furthermore, as the initial strings are not null terminated, the operator << will not work correctly either (will print out arbitrary memory past the string). In general, I would recommend to make the string value always null terminated to avoid some of the issues.
And finally, you definitely need to define operator =, because for now you are using the default one which does a shallow copy, so with this code you will have double-free at destruction of st4 (and any other string assigned with =). Note that implementing the operator = correctly is also tricky - beware of the self-assignment scenario.
There are other issues with your code, most dealing with your comparison operators.
Your operator < and operator== for MyString are incorrect. To compare c-style strings, you use strcmp, not ==. Your comparison operators instead is comparing pointer values, not the data that is being pointed to.
The second issue is that all you need to do is fully implement operator < and operator ==, as all of the other comparison operators can be implemented in terms of these functions.
Putting this all together, the implementation of your class should look something like this:
#include <cstring>
//...
class MyString {
public:
//...
friend bool operator==(const MyString& newWord, const MyString& newWord2);
friend bool operator!=(const MyString& newWord, const MyString& newWord2);
friend bool operator<(const MyString& newWord, const MyString& newWord2);
friend bool operator<=(const MyString& newWord, const MyString& newWord2);
friend bool operator>(const MyString& newWord, const MyString& newWord2);
friend bool operator>=(const MyString& newWord, const MyString& newWord2);
private:
char* value;
int size;
};
bool operator==(const MyString& newWord, const MyString& newWord2)
{ return strcmp(newWord.value, newWord2.value) == 0; }
bool operator<(const MyString& newWord, const MyString& newWord2)
{ return strcmp(newWord.value, newWord2.value) == -1; }
bool operator!=(const MyString& newWord, const MyString& newWord2)
{ return !(newWord == newWord2); }
bool operator<=(const MyString& newWord, const MyString& newWord2)
{ return !(newWord2 < newWord); }
bool operator> (const MyString& newWord, const MyString& newWord2)
{ return newWord2 < newWord; }
bool operator>=(const MyString& newWord, const MyString& newWord2)
{ return !(newWord < newWord2); }
Note that the operators use the < and == to implement the other operators.
The third issue is that you're missing an assignment operator
MyString& operator=(const MyString&)
Without this function, you cannot assign MyString objects to each other without corrupting memory.
I am writing to write a string class and while doing assignment operator overloading I am observing crash at the part where we do deletion of previously allocated memory. I tried to trace through code but couldn't figure it out. Any pointers would help
str& str::operator=(const str &Rhs)
{
if (this != &Rhs)
{
cout << " attempt of self allocation - " << endl;
**delete[] this->_element;** // crashes here
this->_capacity = Rhs._capacity;
this->_display = Rhs._display;
this->_size = Rhs._size;
if (Rhs._size > 0)
{
this->_element = new char(this->_size);
if (this->_element == NULL)
{
cout << " mem allocation failed " << endl;
}
}
for (int counter = 0; counter <= this->_size; counter++)
{
this->_element[counter] = Rhs._element[counter];
}
}
return *this;
}
/*copy constructor */
str::str(const str& Rhs)
{
// copy constructor called
this->_capacity = Rhs._capacity;
this->_display = Rhs._display;
this->_size = Rhs._size;
if (Rhs._size > 0)
{
this->_element = new char(_size);
if (this->_element == NULL)
{
cout << " mem allocation failed " << endl;
}
for (int counter = 0; counter <= this->_size; counter++)
{
this->_element[counter] = Rhs._element[counter];
}
}
}
/* constructor */
str::str(const char *Y)
{
cout << "constructor called !!! -- " << endl;
size_t len = this->stringlen(Y);
this->_element = new char(len + 1);
for (int counter = 0; counter < len; counter++)
{
this->_element[counter] = Y[counter];
}
this->_element[len] = '\0';
this->_size = len + 1;
cout << "string in constructor is -- " << this->_element << endl;
}
From .h file
class str
{
public:
/*Default Constructor*/
explicit str();
/*Constructor with single char Argument*/
explicit str(char x);
/*Constructor with char array Argument*/
explicit str(const char* Y);
/* Creating new element with copy constructor */
str(const str& Rhs);
/* Overloading of Assignment operator */
str& operator=(const str& Rhs);
friend int string_compare(const str& Lhs, const str& Rhs);
int reverse();
size_t stringlen(const char* Y);
str& operator+(str& Rhs);
bool operator==(const str& Rhs);
bool operator!=(const str& Rhs);
friend ostream& operator<<(ostream &out, str& Lhs);
private:
char* _element;
int _capacity;
bool _display;
int _size; //largest accessed + 1
};
Main Routine -
void test1() {
const char *j = "abc";
cout << "length of j = " << strlen(j) << endl;
str s1('U');
str s2("hello");
cout << s2 << endl;
s2.reverse();
cout << s2 << endl;
str s3(s2);
cout << s1 << endl;
cout << s2 << endl;
cout << s3 << endl;
**s2 = s1;** // crashes
cout << s2 << endl;
cout << s1 << endl;
}
There are several problems with your code.
The biggest one is: if you want to allocate an array you need to use new char[_size]. new char(_size) allocates one char with its value set to _size.
Second, once you fix that problem, you write past the end of your allocated array - judging by the comment in your header you need to allocate char[_size + 1].
Third, in your copy constructor you never initialize the _element array and in your assignment operator you never clear up the _element value. This will eventually lead to crash when you copy or assign an empty str and then try to assign to it (or at destruction time as I assume that the destructor also calls delete).