Memory Management : character arrays and = operator - c++

Memory Management : character arrays and = operator
Q. In terms of Memory Management, What error would you have with the following code?
class String
{
public:
String(const char right[]);
String& operator= (const String& right);
int length() const;
private:
char* buffer;
int len;
};
int String::length() const {return len;}
String::String(const char right[])
{
len = 0;
while (right[len] != '\0')
len++;
buffer = new char[len+1];
for (int i = 0; i < len; i++)
buffer[i] = right[i];
buffer[len] = '\0';
}
String& String::operator= (const String& right)
{
int n = right.length();
for (int i = 0; i <= n; i++)
buffer[i] = right.buffer[i];
return *this;
}
Answer.
I have no clue... Could you help me?
The array size seems okay... new operator...
is it because of the dangling pointer because there is no delete operator?
Or rule of three?
I would appreciate your help!
Please let me know.
Thanks,

Is all I can suggest
~String()
{
if(buffer)
delete [] buffer;
len = 0;
}
String(int length)
{
buffer = new char[length];
len = length;
}
String String::operator = (String rhs)
{
if(this != &rhs)
{
delete [] buffer;
buffer = new char[strlen(rhs.m_str) + 1];
strcpy(buffer, rhs.buffer);
}
return *this;
}
If you don't have a destructor, every time you new the buffer in your class, it will be a memory leak because your code has no way of getting rid of it.

Related

writing custom string class in c++

I am trying to write a basic string class for practice, and I am struggling with the reserve() function to increase my char array's memory allocation.
This is the error message I get:
free(): double free detected in tcache 2
This is my approach:
void string::reserve(size_t n)
{
if (n <= space+1) return; // never decrease allocation
char *p = new char[n+1]; // allocate new space
for (int i=0; i<sz; ++i) p[i]=pChars[i]; // copy old elements
//strcpy(p, pChars); // didn't work either
p[n] = '\0';
delete[] pChars; // deallocate old space
pChars = p;
space = n+1;
}
I will also share a picture of my header file below:
Here is the entire code:
#include "String.h"
namespace String
{
string::string(): sz(0), pChars(nullptr), space(0) {}
string::string(const char* s): sz(strlen(s)), pChars(new char[strlen(s)+1]), space(sz+1) ///Constructor using a null-terminated C String
{
//for (int i = 0; s[i] != '\0'; i++) pChars[i] = s[i];
strcpy(pChars,s);
pChars[strlen(s)] = '\0';
}
string::string(const string& s): sz(s.sz), pChars(new char[s.sz]), space(s.sz) ///copy constructor
{
for (size_t i = 0 ; i<s.sz; i++) pChars[i] = s.pChars[i];
pChars[s.sz] = '\0';
}
string& string::operator=(const string& s) ///copy assignment
{
if (this==&s) return *this; // self-assignment, no work needed
if (s.sz<=space)
{ // enough space, no need for new allocation
for (size_t i = 0; i<s.sz; ++i) pChars[i] = s.pChars[i]; // copy elements
sz = s.sz;
return *this;
}
char *p = new char [s.sz]; // copy and swap
for (size_t i = 0; i<s.sz; ++i) p[i] = s.pChars[i];
delete[] pChars;
sz = s.sz;
space = s.sz;
pChars = p;
return *this;
}
string& string:: operator=(const char* s) ///copy assignment using a null-terminated C String
{
char *p = new char[strlen(s)+1];
for(size_t i = 0; s[i] != '\0'; i++) p[i] = s[i];
p[sz] = '\0';
delete [] pChars;
sz = strlen(s);
space = sz;
strcpy(pChars, p);
return *this;
}
///string(string&& s); ///move constructor, not needed (compiler copy elision)
string& string:: operator=(string&& s) ///move assignment
{
delete[] pChars; // deallocate old space
pChars = s.pChars; // copy a's elem and sz
sz = s.sz;
space = s.space;
s.pChars = nullptr; // make a the empty vector
s.sz = 0;
cout << "Move" << endl;
return *this;
}
string::~string()
{
delete [] pChars;
}
char& string:: operator[](size_t pos) {return pChars[pos];} ///set operator
const char& string:: operator[](size_t pos) const {return pChars[pos];} ///get operator
void string::reserve(size_t n)
{
if (n <= space+1) return; // never decrease allocation
char *p = new char[n+1]; // allocate new space
for (int i=0; i<sz; ++i) p[i]=pChars[i]; // copy old elements
//strcpy(p, pChars);
p[sz] = '\0';
delete[] pChars; // deallocate old space
pChars = p;
space = n+1;
}
void string:: push_back(const char c) ///push char c to the end
{
if (sz==0) // no space: grab some
reserve(8);
else if (sz==space) // no more free space: get more space
reserve(2*space);
pChars[sz] = c; // add d at end
++sz; // and increase the size (sz is the number of elements)
}
bool operator==(const string& lhs, const string& rhs) ///check equality (same size, all chars the same)
{
if (lhs.size() != rhs.size()) return false;
else
{
for (size_t i = 0; i<lhs.size(); i++)
{
if (lhs[i] != rhs[i]) return false;
}
}
return true;
}
bool operator==(const string& lhs, const char* rhs) ///equality with null terminated C string
{
if(lhs.size() == strlen(rhs))
{
for (size_t i = 0; i< lhs.size(); i++)
{
if(lhs[i] == rhs[i]) return true;
}
}
return false;
}
bool operator>(const string& lhs, const string& rhs) ///greater than operator, true if lhs > rhs
{
}
string operator+(const string& lhs, const string& rhs) ///concatenate two MyStrings, return the result
{
}
ostream& operator<<(ostream& os, const string& s)
{
for (size_t i = 0; i<s.size(); i++)
{
os << s[i];
}
return os;
}
istream& operator>>(istream& is, string& s)
{
char c;
while(is.get(c))
{
if (c!=' ') s.push_back(c);
}
return is;
}
}
First error
string::string(const string& s): sz(s.sz), pChars(new char[s.sz]), space(s.sz) ///copy constructor
{
for (size_t i = 0 ; i<s.sz; i++) pChars[i] = s.pChars[i];
pChars[s.sz] = '\0';
}
This allocates an array of s.sz chars and then writes off the end of it
SHould be
string::string(const string& s): sz(s.sz), pChars(new char[s.sz] + 1), space(s.sz) ///copy constructor
{
for (size_t i = 0 ; i<s.sz; i++) pChars[i] = s.pChars[i];
pChars[s.sz] = '\0';
}

Array getting wrong amount of spaces

So I've been trying to write string class for learning purposes, and when I tried to overload the + operator I got the strangest problem: The array I've been trying to crate had 4 extra spaces, no matter what amount I tried to create it with (Also the entire thing crashed horribly). So for example, when I wrote:
newStr.length = this->length + other.length;
newStr.str = new char[newStr.length+1];
newStr.str kept having 4 more spaces than this->length + other.length + 1, and when I just left it at newStr.str = new char[1], the array still have been created with 5 cells for some reason. I've tried to write the entire class without ever creating "int length" but that didn't work, nor did any my attempts to isolate the array creation from class(It works normally when not in class and not working with these specific class objects so it's not some visual studio bug). I've tried everything I could think of, though I'm new at this and most likely missed something. Could anybody please help me? Here's the entire code for reference, it's not that long:
#include <iostream>
#define DEBUG
using namespace std;
class MyString
{
private:
char* str;
int length;
public:
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[] str;
}
MyString& operator =(const MyString& other)
{
if (this->str != nullptr) delete[] str;
length = other.length;
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 MyString& other)
{
MyString newStr;
newStr.length = this->length + other.length;
newStr.str = new char[newStr.length+1];
for (int i = 0; i < this->length; i++)
{
newStr.str[i] = this->str[i];
}
for (int i = this->length; i < newStr.length; i++)
{
newStr.str[i] = other.str[i-strlen(this->str)];
}
newStr.str[newStr.length] = '\0';
return newStr;
}
void Print()
{
cout << str;
}
};
int main()
{
MyString a("TESTY");
MyString b("WUBBA");
a = a + b;
a.Print();
return 0;
}

Custom String class adding char* to it in c++

I have a problem with my custom string operator += so that if I wanted to add a const char* to my custom string String, then it wouldn't add it, it would just be the String. I can get it to work with other custom String objects but not with c strings.
code in String.cpp
String::String(){
length =0;
capacity = 1024;
myStr = new char[0];
}
String::String (const String& s)
{
length = s.getLength();
myStr = new char[length];
for (unsigned j=0; j < length; j++){
myStr[j] = s[j];
}
}
String::String(char c){
length = 1;
myStr = new char(c);
}
String::String(const char* val){
if(val!= nullptr){
int counter = 0;
while(val[counter]!='\0')counter++; //Find length of the char array
length= counter;
myStr = new char[counter];
for(int i =0;i<counter;i++){
myStr[i]=val[i];
}
}else{
length = 1;
myStr = new char[length];
myStr[0] = '\0';
}
}
ostream& operator<< (std::ostream& os, const String& s)
{
os<<s.myStr;
return os;
}
istream& operator>> (std::istream& is, String& s)
{
char* c = new char[1000];
is >> c;
s = String(c);
delete[] c;
return is;
}
String::~String(){
delete[] myStr;
}
int String::getIndex(char getInd) const {
for(int i = 0;i<length;i++){
if(myStr[i]==getInd){
return i;
}
}
}
unsigned String::getLength() const{
return length;
}
String& String::operator+= (const String& s){
unsigned len = length+s.getLength();
char* str=new char[len];
for(int i = 0;i<length;i++){
str[i]=myStr[i];
}
for(int i = 0;i<s.length;i++){
str[length+1]=s[i];
}
delete myStr;
length = len;
myStr= str;
return *this;
}
String& String::operator+=(const char *c) {
unsigned l =0;
while(c[l] !='\0'){
l++;
}
unsigned len =length+l;//new length of new string
char* str = new char[len];
for(int i = 0;i<getLength();i++) {
str[i] = myStr[i];
}
for(int j = 0; j<l;j++){
str[len+j]=c[j];
}
delete myStr;
length = len;
myStr = str;
return *this;
}
String& String::operator=(const String& s){
if(this == &s){
return *this;
}
delete myStr;
length = s.getLength();
myStr = new char[length];
for(int i = 0;i<length;i++){
myStr[i]=s[i];
}
}
char& String::operator[](unsigned i){
if(i>=length) throw 1;
return myStr[i];
}
char String::operator[] (unsigned j) const
{
if (j >= length) throw 1;
return myStr[j];
}
Code in main.cpp
int main() {
char c[] = {'h','i'}
String add = "iscool";
String str="strworks";
str+=add;
str+=c;
cout<<str<<endl;
return 0;
}
The outcome for this program right now in the console is:
strworks
I am not sure what to change about the const char* c += operator function, i believe this is where the error is. I am also trying to use this string in a getline function but I try getline(file,String& s, ','); there is an error and I would appreciate any tips on how to implement the getline function with my new String class. The String str should be histrworks in the second line of output, however it is blank. I have already re written the logic of the += operator but it doesn't seem to be working. Let me know if there is any more information I need to give out that could assist you guys in finding the problem. The ultimate goal of this program is to read in a file of movie reviews and train an algorithim to find out if the review is positive/negative.

C++ Dynamic Allocation, Is my use of new correct?

I'm having trouble with dynamic allocation.In my code
am I initializing the dynamic array correctly?.
When I try to write + operator member for my class String, it doesn't return what I want. Any guidance to the correct path would be great.
Ex.
String s1("One");
String s2("Two");
String s3 = s1+ s1;
cout <<s3;
//Output is OneTwo
cout <<s1;
//OUtput is OneTwo.
Also I don't understand why I can not add delete[] buf into my constructor.
class String{
public:
String (const char *s =""):buf(new char[strlen(s)]){
buf = strdup(s);
};
String (const String &s):buf(new char[strlen(s.buf)]){
buf = strdup(s.buf);
delete []buf;
};
String operator =(const String &s){
return buf =strdup(s.buf);
};
char & operator [] (int index){
assert(inBounds(index));
return buf[index];
};
int size()
{
return strlen(buf);
};
String operator + (const String s){
delete []buf;
char *temp = new char[strlen(buf)+strlen(s.buf)];
///NEed to ask about t*his acan get this operator tor work
cout<< s.buf;
return temp;
};
String operator += (const String s){
strcpy(buf + strlen(buf),s.buf);
return buf;
};
void print(ostream & out){
out << buf;
};
void read (istream & in){
in >> buf;
};
~String(){
//delete [] buf;
};
private:
bool inBounds(int x){
return x >= 0 && x < strlen(buf);
};
static int strlen(const char *s){
int len =0;
for(int i=0;s[i] != '\0';i++)
len++;
return len;
};
static char *strcpy(char *dest,const char *src){
int i=0;
for(;(dest[i] = src[i]); ++i);
dest[i] = '\0';
return dest;
};
static char *strdup(const char *s){
char * buf;
buf = new char[strlen(s)+1];
int i=0;
for(;s[i] != '\0';i++)
buf[i] = s[i];
buf[i] = '\0';
return buf;
}
char * buf;
};
Your first constructor
String (const char *s ="") : buf(new char[strlen(s)]){
buf = strdup(s);
}
first allocates a buffer that's one character too small, then it throws it away by pointing bufto the result of strdup – a memory leak.
You want
String (const char *s ="") : buf(new char[strlen(s) + 1]){
strcpy(buf, s);
}
or
String (const char *s ="") : buf(strdup(s))
{
}
Your second constructor
String (const String &s) : buf(new char[strlen(s.buf)]){
buf = strdup(s.buf);
delete []buf;
};
has the same problem with a memory leak, and has the added complication that you immediately deallocate buf.
You want something like
String (const String& s) : buf(strdup(s.buf))
{
}
Your + deallocates buf, allocates an uninitialised (and too small) buffer, prints buf(which is undefined) and then returns a String made from the uninitialised buffer.
The addition operator should not modify *this; it should use += and look like
String operator+ (const String& s) const
{
String result = *this;
result += s;
return result;
};
Which leaves +=, which needs to reallocate buf to be large enough to hold the result.
I'll leave it as an exercise.
And reimplementing standard library functions using the standard name is very confusing.

Creating a custom C type string class in C++

I have an assignment that I need to create a custom C type string class in C++. I am having trouble getting this to work. Currently my code crashes with a run time error right at the start. I also know many of my function are wrong but I want to get the member functions sorted before I go on and fix the other functions. Bear in mind that all the function prototypes were given to us and I can not change them. I need to write the 'guts' so to speak.
What is wrong with my constructor for a start?
#include <iostream>
#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') {
strPtr = new char [strlen(str)+1];
for (i=0; i <strSize;++i) {
strPtr[i] = str[i];
}
++i;
}
strSize = i;
}
//Copy constructor
TStr::TStr(const TStr&) {
}
//Destructor
TStr::~TStr() {
if (strPtr) {
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) {
//this->strPtr += str.strPtr;
//this->strSize += str.strSize;
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 {
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;
}
Thanks for any replies/help! :)
EDIT 1: Okay the constructor is now working but I am still getting a runtime error and I'm 90% sure it is to do with my overloaded += operator. It looks fine though and compiles okay. What am I missing?
(Note: Only small changes have been made to the above code, but let me know if you want to see the whole lot.)
//overload the concatenation oprerator
TStr TStr::operator += (const TStr& str) {
for(int i = 0; i < strSize; ++i) {
strPtr[i] += str.strPtr[i];
}
return *this;
}
EDIT 2: Okay this what I have now. Compiles fine but doesn't actually add the two strings together with the += like it should. Anyone got any ideas?
//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;
}
To summarize:
i is reinitialized in the pointed line
strSize is used uninitialized(God knows what's there) in the same line as above; should be strSize = strlen(str);
the string terminator chracter is wrong
as the man was saying it's kind of a mess
int i=0;
while (str[i] != '\0') { // as Seth pointed out it's '\0'
strPtr = new char [strlen(str)+1];
for (i=0; i <strSize;++i) { // i is reinitialized here !!!
strPtr[i] = str[i];
}
++i;
}
strSize = i;
To be more constructive:
// as James perfectly illustrated
TStr::TStr(const char *str)
{
int i = 0;
while (str[i] != '\0')
++i;
strSize = i;
strPtr = new char [i+1];
while (*strPtr++ = *str++); // with a bit of k&R
}
//overload the concatenation oprerator
TStr TStr::operator += (const TStr& str) {
for(int i = 0; i < strSize; ++i) {
strPtr[i] += str.strPtr[i];
}
return *this;
}
Problems:
you want to concatenate strings meaning you need a bigger storage to hold both strings together meaning you need to reallocate your char array and you don't do that
you don't update the size of your string, it's bigger now isn't it?
strPtr[i] += str.strPtr[i]; what you're doing here is really adding integers stored on 8 bits
Solution(I'm absolutely sure it can be improved but should get you started):
//overload the concatenation oprerator
TStr TStr::operator += (const TStr& str) {
unsigned int i = 0;
while (str.strPtr[i] != '\0')
++i;
// allocate the new buffer
char* newStr = new char[i + strSize + 1];
// copy the old string
unsigned int j = 0;
for (; j < strSize; ++j)
{
newStr[j] = strPtr[j];
}
// update the size
strSize += i;
// release the old buffer
delete[] strPtr;
// finally concatenate
char* copyPtr = newStr + j;
while(*copyPtr++ = *(str.strPtr)++);
// and swap the pointers
strPtr = newStr;
return *this;
}
Your ctor is pretty much a mess.
You use i for two different things -- at the same time. You also copy the entire contents of str into strPtr once for each character in str.
Basically, you have to decide, are you going to use the C run-time library or not?
Using it:
TStr::TStr(const char *str)
{
strSize = strlen(str);
strPtr = new char [strSize+1];
strcpy(strPtr, str);
}
not using it:
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];
}
Why two loops one inside the other? You're thinking too hard, to copy characters from one string to another you only need one loop. Here's some code
//constructor; conversion from the char string
TStr::TStr(const char *str) {
strSize = strlen(str);
strPtr = new char [strSize+1];
for (int i=0; i <strSize; ++i) {
strPtr[i] = str[i];
}
strPtr[strSize] = '\0';
}
Much simpler!