Assertion `str' failed - c++

I am new bee for c++ . I got follow error when I override operator+ .
ConsoleApplication1.out: /root/projects/ConsoleApplication1/sdstring.cpp:43: static sdstring::size_t sdstring::strlen(const char*): Assertion `str' failed.
This is my test code!
sdstring sd1(NULL);
cout << "sd1:" << sd1 << endl;
sdstring sd2("sd2");
cout << "sd2:" << sd2 << endl;
sdstring sd3;
cin >> sd3;
cout << "sd3:" << sd3 << endl;
sd3 +=sd2 ;
cout << "sd3:" << sd3 << endl;
sdstring sd4 =sd3+sd1;
cout << "sd4:" << sd2 << endl;
sd1 = sd2 + sd1;
cout << "sd1:" << sd1 << endl;
cout << "sd3==sd2:" << (sd3 == sd2)<<endl;
Error happened in This line .
sdstring sd4 =sd3+sd1;
This is my sdstring.cpp file.
#include "sdstring.h"
#include <assert.h>
sdstring::sdstring(const char *str) {
if (!str) {
datas = new char[1];
datas[0] = '\0';
}
else {
datas = new char[strlen(str)+1];
strcpy(datas, str);
}
}
sdstring::sdstring(const sdstring& str) {
datas = new char[strlen(str.datas) + 1];
strcpy(datas, str.datas);
}
sdstring& sdstring::operator+(const sdstring& str)const {
sdstring result(NULL);
size_t total_size = getlen() + str.getlen();
result.datas = new char[total_size + 1];
strcpy(result.datas, datas);
strcat(result.datas, str.datas);
return result;
}
bool sdstring::operator==(const sdstring& str)const {
return strcmp(datas, str.datas) == 0;
}
sdstring& sdstring::operator=(const sdstring& str) {
if (this == &str)
return *this;
delete[] datas;
datas = new char[str.getlen() + 1];
strcpy(datas, str.datas);
return *this;
}
sdstring::size_t sdstring::strlen(const char* str) {
assert(str);
size_t len = 0;
while ('\0' != *str++)
len++;
return len;
}
char* sdstring::strcpy( char* des, const char* src){
assert(des&& src);
char* temp = des;
while ('\0' != (*des++ = *src++));
return temp;
}
int sdstring::strcmp(const char* fir, const char* sec) {
assert(fir && sec);
while (*fir == *sec)
{
if (*fir == '\0') {
return 0;
}
++fir;
++sec;
}
return *fir - *sec;
}
char* sdstring::strcat(char* des,const char* src) {
char* temp = des;
while ('\0' != *des)
{
des++;
}
while ('\0' != (*des++ = *src++));
return temp;
}
sdstring::~sdstring()
{
if (datas)
{
delete[] datas;
datas = nullptr;
}
}
char& sdstring::operator[](const unsigned int position)const
{
return position < getlen() ? datas[position] : datas[position-1];
}
sdstring& sdstring::operator+=(const sdstring& str)
{
size_t total_size = getlen() + str.getlen();
if (total_size != getlen()) {
char* temp = datas;
datas = new char[total_size + 1];
strcpy(datas,temp);
strcat(datas, str.datas);
delete[] temp;
}
return *this;
}
ostream& operator<<(ostream& os, const sdstring& str)
{
os << str.datas;
return os;
}
istream& operator>>(istream& is, sdstring& str)
{
char* cache = new char[1024];
is >> cache;
delete[]str.datas;
str.datas = new char[sdstring::strlen(cache)];
sdstring::strcpy(str.datas, cache);
delete[]cache;
return is;
}
Who can help me , Thanks for first!

There are several things wrong with your code. However the operator + is wrong in that it is returning a reference to a local variable, which is undefined behavior.
sdstring& sdstring::operator+(const sdstring& str)const
{
sdstring result(NULL);
//..
return result; // Undefined behavior.
}
operator + should be returning a brand new object, not a reference to an object.
sdstring sdstring::operator+(const sdstring& str)const // <-- Note the return value is sdstring
{
sdstring result(NULL);
//..
return result;
}
The other issues with your code:
1) The operator= destroys the memory using delete[] datas; before calling new[] to allocate memory for the new string. If new[] throws an exception, the sdstring object would be corrupted since the data has been destroyed and you can't go back and reset the string with the old data. Use the copy / swap idiom to prevent this from happening.
2) You should store the length of the string in a member variable instead of calling strlen every time you want to know the length of the string. The strlen is slow in that it has to loop to count every single character to determine where the terminating '\0' character is located. Instead of that, just store the length of the string once and keep using that value.
3) Instead of writing your own strlen and strcmp functions, use the library functions strlen and strcmp. Your versions are not optimized, unlike the library versions.
4) operator + can be written in terms of operator +=. operator + should be written very simply as:
sdstring sdstring::operator + (const sdstring& rhs)
{
return sdstring(*this) += rhs;
}

Related

Why do I keep getting a segmentation fault with my delete function

I have an assignment where I need to use an overloaded destructor to delete dynamically allocated pointers. However, when it runs, some of my pointers are deleted until a segmentation fault with one of my objects' pointers the one pointing to " and the second" made with a parameterized constructor.
I have tried to go through and make sure the delete operator had brackets (because my new operator did) I made sure the object still existed by printing out its information and address. I have tried to rewrite my allocation function, and I have tried to go over my destructor to see where it messes up.
If it helps, I have included my destructor, my allocation function, my deallocation function, and my parameterized constructor.
'''
//Destructor
MyString::~MyString()
{
buffer_deallocate();
};
void MyString::buffer_deallocate() {
cout << m_buffer << endl;
delete[](m_buffer);
m_buffer = NULL;
m_size = 0;
}
void MyString::buffer_allocate(size_t size) {
try {
m_buffer = new char[size];
m_size = size;
}
catch(bad_alloc&)
{
cout << "Errror: Unable to allocate memory" << endl;
buffer_deallocate();
}
}
//Parameterized Constructor
MyString::MyString(const char * str)
:m_size(0)
{
const char * strPtr = str;
while(*strPtr)
{
strPtr++;
m_size++;
}
buffer_allocate(m_size);
for(int i = 0; i < m_size; i++)
{
m_buffer[i] = str[i];
}
};
'''
Every time however I get the output after "and the second" to be
Segmentation fault (core dumped)
Edit: I have tried the majority of what was recommended. At least what I understood, the problem still persists, and I realize now I was kind of sparse on my code. (Please forgive me, I'm still learning.) Here is the new code along with the rest of the function file for reference:
'''
#include<iostream>
#include<string.h>
using namespace std;
#include"MyString.h"
//Default Constructor
MyString::MyString()
:m_size(0), m_buffer(NULL)
{
buffer_allocate(0);
};
//Parameterized Constructor
MyString::MyString(const char * str)
:m_size(strlen(str)+1), m_buffer(NULL)
{
buffer_allocate(m_size);
strncpy(m_buffer, str, m_size);
};
//Copy Constructor
MyString::MyString(const MyString & other)
:m_size(0), m_buffer(NULL)
{
const char * otherPtr = other.c_str();
buffer_allocate(other.size());
for(int i = 0; i < size(); i++)
{
m_buffer[i] = otherPtr[i];
}
m_buffer[m_size] = '\0';
};
//Destructor
MyString::~MyString()
{
buffer_deallocate();
};
size_t MyString::size() const
{
return m_size;
}
size_t MyString::length() const{
return m_size-1;
}
const char * MyString::c_str() const{
return m_buffer;
}
bool MyString::operator==(const MyString & other) const {
char * m_bufferPointer = m_buffer;
while(*m_bufferPointer++)
{
const char * str_ptr = other.c_str();
if(*m_buffer != *str_ptr++)
{
return 0;
}
}
return 1;
}
MyString & MyString::operator=(const MyString & rhs) {
buffer_deallocate();
buffer_allocate(rhs.size());
const char * c_strPtr = rhs.c_str();
int i;
for(i = 0; i < rhs.size(); i++)
{
this->m_buffer[i] = c_strPtr[i];
}
return *this;
}
MyString MyString::operator+ (const MyString & other_myStr) const {
char * temp_pointer;
temp_pointer;
size_t temp_size = m_size + other_myStr.size();
//New Combined Buffer for Concatanation
try {
temp_pointer = new char[temp_size];
temp_pointer = strcat(this->m_buffer, other_myStr.c_str());
}
catch(bad_alloc&)
{
cout << "Error: Unable to Allocate Memory";
return NULL;
}
return MyString(temp_pointer);
}
char & MyString:: operator[](size_t index) {
return m_buffer[index];
}
const char & MyString::operator[] (size_t index) const {
return m_buffer[index];
}
ostream & operator << (ostream& os, const MyString & myStr) {
os << myStr.m_buffer;
return os;
}
void MyString::buffer_deallocate() {
cout << "Trying to delete : " <<m_buffer << endl;
if(m_buffer){
delete[](m_buffer);
}
cout << " Success" <<endl;
m_buffer = NULL;
m_size = 0;
}
void MyString::buffer_allocate(size_t size) {
try {
m_buffer = new char[size];
m_size = size;
}
catch(bad_alloc&)
{
cout << "Errror: Unable to allocate memory" << endl;
m_size = 0;
}
}
'''
in MyString::buffer_deallocate
cout << m_buffer << endl;
requires that m_buffer be null-terminated. Unfortunately, MyString::MyString(const char * str) does not make this guarantee.
You could
for(int i = 0; i < m_size; i++)
{
cout << m_buffer[i] << endl;
}
to print the string out character by character instead, but it is probably more useful to waste a byte, null terminate, and take advantage of the Standard library
MyString::MyString(const char * str)
:m_size(0)
{
const char * strPtr = str;
while(*strPtr)
{
strPtr++;
m_size++;
}
buffer_allocate(m_size);
for(int i = 0; i < m_size; i++)
{
m_buffer[i] = str[i];
}
m_buffer[m_size] = '\0'; // add the null
}
and then
void MyString::buffer_allocate(size_t size) {
try {
m_buffer = new char[size+1]; // +1 for the null terminator
m_size = size;
}
catch(bad_alloc&) // this is a bad thing to do here. More on that later.
{
cout << "Errror: Unable to allocate memory" << endl;
buffer_deallocate();
}
}
But we can simplify this with a few library function calls.
MyString::MyString(const char * str)
:m_size(strlen(str))
{
buffer_allocate(m_size);
strcpy(m_buffer, str);
}
Addendum:
Your class may be violating the Rule of Three. If MyString does not have a copy constructor and an assignment operator to go along with the destructor any copies, deliberate or accidental, will turn a MyString into a time bomb. The destructor of one of the copies will be run before the others leaving the others without a valid m_buffer.
MyString::buffer_allocate cannot safely return void unless it allows the exception to propagate. Catching the bad_alloc leaves the object without a valid allocation at m_buffer and the rest of the program will not know this. Either every other access in the program must test for a valid buffer or engage in undefined behaviour trying to access the invalid memory. It's likely better to let the exception fall through and be caught by another part of the program that is better suited to making a decision on what to do.
MyString::buffer_allocate will leak an existing allocation if called on a MyString that already has a valid allocation at m_buffer. I recommend a
if (m_buffer)
{
delete[] m_buffer;
}
and initializing m_buffer to null in MyString's constructor
MyString(const char* str)
: m_size(std::strlen(str)), m_buffer(nullptr)
{
buffer_allocate(m_size);
std::strncpy(m_buffer, str, m_size);
}
So I had to remake this to some working code.
The problem might be that a string length must be extended with 1 to add the nul-termination. So that makes:
// prevent some MSVS warnings->errors
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
#include <cstring>
//Destructor
class MyString {
private:
size_t m_size;
char* m_buffer;
void buffer_allocate(size_t size) {
try {
m_buffer = new char[size];
m_size = size;
}
catch (std::bad_alloc&)
{
std::cout << "Errror: Unable to allocate memory\n";
// allocation failed, nothing changed, don't delete/
m_size = 0;
}
}
void buffer_deallocate() {
// printing in the buffer deallocation??
delete[] m_buffer;
m_buffer = nullptr;
m_size = 0;
}
public:
MyString(const char* str)
: m_size(std::strlen(str)+1) // add null termination
, m_buffer(nullptr)
{
buffer_allocate(m_size);
std::strncpy(m_buffer, str, m_size);
}
~MyString() {
buffer_deallocate();
}
void Print() const {
std::cout << m_buffer << '\n';
}
};
int main() {
std::string input{ "Hello World!" };
MyString myString(input.c_str());
myString.Print();
}

String & String::Concat(const char Str[])

I decided to code all assignments from last semesters c++ class over summer in order to better prepare for c++ 3 but I don't understand how to pass through a String class or what steps are even needed in order to concatenate two strings and the display the result in the main cpp file.
In my Main.cpp:
#include <iostream>
using namespace std;
#include "String.h"
int main()
{
String Str1;
String Str2("this is a test");
String Str3(Str2);
String Str4("bruh");
int result;
cout << "Testing Display: " << endl;
Str2.Display();
cout << endl;
cout << "Testing displayLine: " << endl;
Str2.displayLine();
cout << endl;
result = Str2.Compare(Str3);
if (result < 0)
{
Str2.Display();
cout << " comes before " << endl;
Str3.Display();
cout << endl;
}
else
if (result > 0)
{
Str3.Display();
cout << " comes before " << endl;
Str2.Display();
}
else
{
Str3.Display();
cout << " is equal to " << endl;
Str2.Display();
}
cout << endl;
result = Str2.Compare("wxyz");
Str1.Copy(Str3);
cout << "Str1 contains " << Str1.length() <<" characters"<< endl;
cout << "Concatenation: ";
Str2.Concat(Str4);
cout << endl;
return 0;
}
In my String.cpp:
#include <iostream>
using namespace std;
#include <string.h>
#include "String.h"
#pragma warning(disable:4996)
String::String()
{
NumChars = 0;
MaxSlots = 0;
pChar = new char[NumChars+1];
pChar[0] = '\0';
}
String::String(const char Str[])
{
NumChars = strlen(Str);
pChar = new char[NumChars + 1];
strcpy(pChar, Str);
}
String::String(const String & Str)
{
NumChars = Str.NumChars;
pChar = new char[NumChars + 1];
strcpy(pChar, Str.pChar);
}
String::~String()
{
delete[] pChar;
}
int String::Compare(const String & Str) const
{
return strcmp(pChar, Str.pChar); //case sensitive
}
int String::Compare(const char Str[]) const
{
return strcmp(pChar, Str); //case sensitive
}
String& String::Copy(const String & Str)
{
if (this != &Str)
{
if (MaxSlots < Str.NumChars)
{
delete[]pChar;
MaxSlots = Str.NumChars;
pChar = new char[NumChars + 1];
}
else;
NumChars = Str.NumChars;
strcpy(pChar, Str.pChar);
}
else;
return *this;
}
String& String::Copy(const char Str[])
{
delete[] pChar;
NumChars = strlen(Str);
MaxSlots = NumChars;
pChar = new char[MaxSlots + 1];
return *this;
}
String& String::Concat(const String & Str)
{
pTemp = new char[NumChars+1];
strcpy(pTemp, pChar);
strcat(pTemp, Str.pChar);
delete[]pChar;
pChar = pTemp;
return *this;
}
String & String::Concat(const char Str[])
{
return *this;
/*
NumChars = strlen(Str);
MaxSlots = NumChars;
delete[] pChar;
MaxSlots = MaxSlots + NumChars;
NumChars = NumChars + strlen(Str);
pChar = new char[MaxSlots + 1]; */
}
void String::Display() const
{
cout << pChar;
}
void String::displayLine() const
{
cout << pChar;
}
In my String.h:
#ifndef STRING_H
#define STRING_H
class String
{
public:
String(); //default constructor
String(const char[]);
String(const String &); //copy constructor
~String();
int Compare(const String &) const;
int Compare(const char[])const;
String& Copy(const String&);
String& Copy(const char[]);
String& Concat(const String&);
String& Concat(const char[]);
void Display()const;
void displayLine() const;
int length() const;
private:
char * pChar;
char *pTemp;
int NumChars;
int MaxSlots;
};
inline int String::length() const
{
return NumChars;
};
#endif
You expect the length of the concatenated string to be the sum of the length of the two strings. Therefore:
String& String::Concat(const String & Str)
{
pTemp = new char[NumChars + Str.NumChars + 1];
strcpy(pTemp, pChar);
strcat(pTemp, Str.pChar);
delete[]pChar;
pChar = pTemp;
return *this;
}
You can optimize this further by not strcat()-ing but strcpy()-ing twice (with an offset added to pTemp second time), as you already know the string length.
I'm not sure to understand the use of your MaxSlots; it's the size of allocated pChar (minus 1)? If so, there are points in your code where you forget to set/update/use.
And I don't understand what do you mean with "how to pass through a String class", but regarding "what steps are even needed in order to concatenate two strings", you've forgotten to take in count the number of chars already present in the object.
First of all, I suggest to create e Reserve() method; something like [caution: code not tested]
String& String::Reserve (int n)
{
if ( n > MaxSlots )
{
MaxSlots = n;
pTemp = new char[MaxSlots+1];
strcpy(pTemp, pChar);
delete[]pChar;
pChar = pTemp;
}
}
Next, rewrite your Concat() methods in this way
String& String::Concat(const String & Str)
{
NumChars += Str.NumChars;
Reserve(NumChars);
strcat(pChar, Str.pChar);
return *this;
}
String & String::Concat(const char * Str)
{
if ( Str )
{
NumChars += strlen(Str);
Reserve(NumChars);
strcat(pChar, Str);
}
return *this;
}
p.s.: sorry for my bad English

Read Access Violation _First was nullPtr

I have a default constructor, copy constructor, destructor, assignment operator, length, and string functions. When I call the copy constructor and try to print the value of the new char*, I get a Read Access Violation _First was nullptr. The debugger shows iosfwd code and breaks at the error, but I have no clue what this means. Any help would be awesome.
This is where the debugger shows the error.
static size_t __CLRCALL_OR_CDECL length(const _Elem *_First)
{ // find length of null-terminated string
return (*_First == 0 ? 0 // <- this line
: _CSTD strlen(_First));
}
These are my functions (also not that we were not allowed to use STRCPY)
MyString::MyString() { //default constructor
string = new char[6];
int i = 0;
for (i; i < strlen(string); i++) {
string[i] = NULL;
}
string[i] = '\0';
}
MyString::MyString(const MyString &s) { //copy constructor
char* string = new char[strlen(s.string) + 1];
int i = 0;
for (i; i < strlen(s.string); i++) {
string[i] = s.string[i];
}
string[i] = '\0';
}
MyString::~MyString() { //destructor
delete[] string;
string = NULL;
}
MyString& MyString::operator=(const MyString& s) { //assignment operator
char* temp = new char[strlen(s.string) + 1];
int i = 0;
for (i; i < strlen(s.string); i++) {
temp[i] = s.string[i];
}
temp [i] = '\0';
delete[] string;
string = temp;
return *this;
}
size_t MyString::length() const { //length of string
return strlen(string);
}
char* MyString::cString() { //string
return string;
}
int main(int argc, const char * argv[]){
MyString s;
std::cout << "Empty: '" << s.cString() << "'\n";
s = "hello";
std::cout << "After operator=: '" << s.cString() << "'\n";
{
MyString t(s);
std::cout << "After copy: '" << t.cString() << "'\n";
s = t;
}
}
This line in your copy constructor
char* string = new char[strlen(s.string) + 1];
Creates a new local variable called string which means that your member variable called string is never assigned a value.
Remove the char *

Problems with Custom C type string class

I have homework that I need to make a custom string class using C type strings. Most of it seems okay but I am getting a runtime error as soon as I test it and it isn't performing all my tests like it should. Specifically my += operator seems to have something wrong with it but I can't understand what.
I can't add in or change any of the prototypes and I should use C++ constructs over C type where possible.
Thanks!
#include <iostream>
#include <string>
#include "tstr.h"
using namespace std;
//Default constructor to initialize the string to null
TStr::TStr() {
strPtr = 0;
strSize = 0;
}
//constructor; conversion from the char string
TStr::TStr(const char *str) {
int i=0;
while (str[i] != '\0') {
++i;
}
strSize = i;
strPtr = new char [i+1];
for (i=0; i < strSize; ++i) {
strPtr[i] = str[i];
}
}
//Copy constructor
TStr::TStr(const TStr& str) {
strPtr = new char[str.strSize];
strcpy(strPtr, str.strPtr);
}
//Destructor
TStr::~TStr() {
delete[] strPtr;
}
//subscript operators-checks for range
char& TStr::operator [] (int i) {
assert (i >= 0 && i < strSize);
return strPtr[i];
}
const char& TStr::operator [] (int i) const {
assert (i >= 0 && i < strSize);
return strPtr[i];
}
//overload the concatenation oprerator
TStr TStr::operator += (const TStr& str) {
char *buffer = new char[strSize + str.strSize + 1];
strcpy(buffer, strPtr);
strcat(buffer, str.strPtr);
delete [] strPtr;
strPtr = buffer;
return *this;
}
//overload the assignment operator
const TStr& TStr::operator = (const TStr& str) {
if (this != &str) {
delete[] strPtr;
strPtr = new char[strSize = str.strSize];
assert(strPtr);
for (int i=0; i<strSize; ++i) {
strPtr[i] = str.strPtr[i];
}
}
return *this;
}
//overload two relational operators as member functions
bool TStr::operator == (const TStr& str) const {
int value = strcmp(strPtr, str.strPtr);
if (value == 0) {
return true;
} else {
return false;
}
/*int counter=0;
for (int i=0; i < strSize; ++i) {
if (strPtr[i] == str.strPtr[i]) {
++counter;
}
}
if (counter == strSize) {
return true;
} else {
return false;
}
return (strPtr == str.strPtr && strSize == str.strSize);*/
}
bool TStr::operator < (const TStr& str) const {
return (strPtr < str.strPtr && strSize < str.strSize);
}
//the length of the string
int TStr::size() {
return strSize;
}
//Overload the stream insertion and extraction operators.
ostream& operator << (ostream& out, const TStr& str) {
int size = str.strSize;
for (int i=0; i < size; ++i) {
out << str[i];
}
return out;
}
istream& operator >> (istream& in, TStr& str) {
return in;
}
//overload two other relational operators as global functions
bool operator != (const TStr& S1, const TStr& S2) {
return !(S1 == S2);
}
bool operator <= (const TStr& S1, const TStr& S2) {
return (S1 < S2 || S1 == S2);
}
bool operator > (const TStr& S1, const TStr& S2) {
return !(S1 < S2);
}
bool operator >= (const TStr& S1, const TStr& S2) {
return !(S1 < S2 || S1 == S2);
}
//overload the concatenation operator as a global function
TStr operator + (const TStr& str1, const TStr& str2) {
//return (str1 += str2);
//return (str1 + str2);
}
This is what I'm testing with:
int main() {
authors();
TStr str1 = "VENI"; //initialize str1 using
//the assignment operator
const TStr str2("VEDI"); //initialize str2 using the
//conversion constructor
TStr str3; //initialize str3 to null
TStr str4; //initialize str4 to null
cout << "\nTest 1: str1: " << str1 << " str2 " << str2
<< " str3 " << str3 << " ###.\n" ; //Test 1
if (str1 <= str2) //Test 2
cout << "\nTest 2: " << str1 << " is less "
<< "than " << str2 << endl;
else
cout << "\nTest 2: " << str2 << " is less "
<< "than " << str1 << endl;
str1=" Pride is what we have.";
str3 = str1; //Test 3
cout << "\nTest 3: The new value of str3 = "
<< str3 << endl;
str3 += " Vanity is what others have. ";
cout<<"\nTest 4: The str3: '" << str3<<"' \nhas "; //test 4
cout<< countVowels(str3)<<" vowels "<< endl;
/*TStr str5 = str1 + str2 + str3;
cout<<"\nTest 5: The str5: '" << str5<<"' \nhas "; //test 5
cout<<countVowels(str5)<<" vowels \n";
cout<<"\nTest 6: The str3 again: " << str3 <<endl; //test 6
cout<<"\n\n Bye, Bye!";*/
return 0;
}
EDIT: I should have added that I need to get the tests to work as part of the homework.
EDIT 2: Here are the functions I changed so far.
//constructor; conversion from the char string
TStr::TStr(const char *str) {
int i=0;
while (str[i] != '\0') {
++i;
}
strSize = i;
strPtr = new char [i+1];
for (i=0; i < strSize; ++i) {
strPtr[i] = str[i];
strPtr[i + 1] = '\0'; //<- this
}
}
//Copy constructor
TStr::TStr(const TStr& str) {
strPtr = new char[str.strSize + 1]; //<-this
strcpy(strPtr, str.strPtr);
}
//overload the concatenation oprerator
TStr TStr::operator += (const TStr& str) {
char *buffer = new char[strSize + str.strSize + 1]; //<- this
strcpy(buffer, strPtr);
strcat(buffer, str.strPtr);
delete [] strPtr;
strPtr = buffer;
return *this;
}
//overload the assignment operator
const TStr& TStr::operator = (const TStr& str) {
if (this != &str) {
delete[] strPtr;
strPtr = new char[strSize = str.strSize + 1]; //<- this
assert(strPtr);
for (int i=0; i<strSize; ++i) {
strPtr[i] = str.strPtr[i];
}
}
return *this;
}
EDIT 3: Unfortunately changing it to below did nothing really. It might be the = operator that's causing the problem as it now fails just after test 3.
//constructor; conversion from the char string
TStr::TStr(const char *str) {
int i=0;
while (str[i] != '\0') {
++i;
}
strSize = i;
strPtr = new char [i+1];
strcpy(strPtr, str);
}
You have several problems involving the null-terminator with several of your functions(some of these you have fixed in your edits, but I'm just going to cover them all anyway, so that this is a thorough answer).
In the constructor converting from const char *, you are not copying over the null-terminator to strPtr. (fixed)
In your copy constructor, you are not allocating enough room for the null-terminator. (fixed)
In your assignment operator, you are not allocating enough room for the null-terminator. (fixed ... not exactly, see #5)
In your assignment operator, you are not copying over the null-terminator.
You added a new problem in your assignment operator when you fixed the allocation. You are now setting strSize to the wrong size.
There are several places in which you need +1 to reserve the ending \0 for strings. For example, in the copy constructor
strPtr = new char[str.strSize];
(that should be str.strSize+1) and in operator=(). These may be the sources of your problems.
BUT you should carefully review all these kind of errors, and test each function separately (maybe with a good test suite, you know, written before the class itself) :)
Your char* converting constructor doesn't NUL-terminate the content (it reserves space for a NUL, but doesn't store one), which causes strcpy and strcat to fail later.
Since no one has pointed it out yet, your strSize remains undefined in your copy constructor; and in your assignment operator.
You only need to null terminate your strings if they will be interfacing with C style strings (ie char*, strcopy etc).
operator +=() should return a reference -- TStr&. You are returning a copy, which is probably not what you want.

Odd output from program when entering operator?

I'm probably missing something obvious here. This is my code (I'm just learning true C++, and I want to get some practice):
#include <iostream>
#include <cstring>
using namespace std;
class String {
private:
char * value;
int len;
friend ostream & operator<<(ostream & os, String s);
public:
String();
String(const char * base);
~String();
String operator+(String s);
String operator*(int n);
int length();
};
String::String() {
this->value = new char[0];
this->len = 0;
}
String::String(const char * base) {
this->value = new char[this->len = strlen(base)];
strcpy(this->value, base);
}
String::~String() {
delete [] this->value;
}
int String::length() {
return this->len;
}
String String::operator+(String s) {
String n;
delete [] n.value;
cout << "Upon entering, I am: \"" << *this << "\"\n";
n.value = new char[this->len + s.len];
for(int i = 0; i < this->len; i++) {
n.value[i] = this->value[i];
}
for(int i = 0; i < s.len; i++) {
n.value[i + this->len] = s.value[i];
}
n.len = this->len + s.len;
cout << "String::operator+(" << *this << ", " << s << ") succeeded with new value = \"" << n << "\"\n";
return n;
}
String String::operator*(int n) {
String s;
delete [] s.value;
s.value = new char[this->len * n];
for(int i = 0; i < this->len * n; i++) {
s.value[i] = this->value[i % this->len];
}
cout << "String::operator* succeeded with new value = \"" << s << "\"\n";
return s;
}
ostream & operator<<(ostream & os, String s) {
return os << s.value;
}
int main() {
String s("Hello, world!");
cout << s << "\nLength = " << s.length() << "\n\n";
cout << (s + String("\n")) * 5;
return 0;
}
And the string initializes and displays correctly, but my output is really strange; it seems that upon entering the operator+, "Hello, world!" suddenly becomes "x%r"?
C:\Users\Ryan\Documents\My Dropbox\C++ Projects>strings
Hello, world!
Length = 13
Upon entering, I am: "x%r"
String::operator+(x%r,
) succeeded with new value = "x%r"
String::operator* succeeded with new value = "╚%r"
─
Try this:
ostream & operator<<(ostream & os, const String& s) {
return os << s.value;
}
otherwise your should define copy constructor for your String class.
You need to provide copy constructor and assignment operator.
There are a lot of problems with your code.
You are managing your own memory. You should avoid doing this if at all possible.
You are consitantly forgetting that strings have a null terminator. In order to accomodate the strin Hello, world! you need a char buffer that is 14 bytes, not 13.
You have a len member variable that does effectively the same thing as the strlen function, except for the inconsistent consideration of #1 above.
Your string class does not have a copy constructor, which results in wild pointers and eventually crashes.
Here is a refactoring of your code that pretty much works.
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstring>
using namespace std;
class String {
private:
char * value;
// size_t len;
friend ostream & operator<<(ostream & os, String s);
public:
String();
String(const char * base);
String(const String& rhs)
{
value = new char[strlen(rhs.value)+1];
strcpy(value,rhs.value);
}
~String();
String operator+(String s);
String operator*(int n);
size_t length();
};
String::String() {
this->value = new char[0];
}
String::String(const char * base) {
this->value = new char[strlen(base)+1];
strcpy(this->value, base);
}
String::~String() {
delete [] this->value;
}
size_t String::length() {
return strlen(value);
}
String String::operator+(String s) {
String n;
delete [] n.value;
cout << "Upon entering, I am: \"" << *this << "\"\n";
n.value = new char[strlen(value)+strlen(s.value)+1];
for(int i = 0; i < strlen(value); i++) {
n.value[i] = this->value[i];
}
for(int i = 0; i < strlen(s.value); i++) {
n.value[i + strlen(value)] = s.value[i];
}
n.value[strlen(value)+strlen(s.value)] = '\0';
cout << "String::operator+(" << *this << ", " << s << ") succeeded with new value = \"" << n << "\"\n";
return n;
}
String String::operator*(int n) {
String s;
delete [] s.value;
s.value = new char[(strlen(value)*n)+1];
for(int i = 0; i < strlen(value) * n; i++) {
s.value[i] = this->value[i % strlen(value)];
}
s.value[strlen(value)*n] = '\0';
cout << "String::operator* succeeded with new value = \"" << s << "\"\n";
return s;
}
ostream & operator<<(ostream & os, String s) {
return os << s.value;
}
int main() {
String s("Hello, world!");
cout << s << "\nLength = " << s.length() << "\n\n";
cout << (s + String("\n")) * 5;
return 0;
}