I'm making my own string class but I got a problem reading the characters of the string while using strlen() to do that.
/****str.h****/
class str
{
private:
char *m_ptr;
unsigned int m_size;
//unsigned int m_capacity;
public:
str();
str (const char *);
str (const str &);
~str (){if (m_size != 0) delete [] m_ptr;};
char *data() {return m_ptr;};
//Sobrecarga de operadors.
str operator=(str);
friend std::ostream& operator<<(std::ostream &, str);
};
I got the error using the constructor initialized with a c-string constant.
/****str.cpp****/
//Default constructor.
str :: str ()
{
m_ptr = new char [1];
m_ptr[0] = '\0';
m_size = 0;
//m_capacity = 10;
}
str :: str(const char *sm_ptr)
{
m_size = strlen(sm_ptr); //HERE IS WHERE THE ERROR OCCURS.
m_ptr = new char[m_size + 1];
strcpy(m_ptr, sm_ptr); //Copies the C string pointed by source into the array pointed by destination, including the terminating null character
}
//Copy constructor.
str :: str(const str &right)
{
m_ptr = new char [right.m_size];
strcpy (m_ptr, right.m_ptr);
m_size = right.m_size;
}
str str::operator=(str right)
{
if (m_size != 0) delete [] m_ptr;
m_ptr = new char [right.m_size + 1];
strcpy(m_ptr, right.m_ptr);
m_size = right.m_size;
return *this;
}
std::ostream &operator<<(std::ostream &strm, str obj)
{
strm << obj.m_ptr;
return strm;
}
0x0053fdd0 {m_ptr=0xcccccccc m_size=3435973836 } str *
Changing the assignment operator declaration to
str& operator=(str&);
or even
const str& operator=(const str&);
would eliminate creation of temporary objects. Check out this article for more information on passing arguments to functions.
There are a couple of other issues. For example in default constructor you allocate memory but you don't set size so it's never going to be freed. Also, in copy constructor and assignment operator it's almost always a good idea to check for self-assignment.
Related
I have an assignment where I build my own my string class. I made several MYString objects and put them in a vector. When I access the vector through subscript operator, I have no issue. But when I traverse through the vector using foreach loop, then I get a weird error ""Pointer being freed was not allocated"
Did I mess up my copy constructor?
Is my destructor and clear() function messed up?
header file is as follows:
class MYString{
friend std::ostream& operator<<(std::ostream&, const MYString&);
friend std::istream& operator>>(std::istream&, MYString&);
private:
static const int INITIAL_CAP = 20;
char* str;
static int getLength(const char*);
int cap; //capacity of the char array, in multiples of 20
int end; // location of the null terminator
int compareTo(const MYString& rhs);
void clear(); // to manually free memory
static int requiredCap(int end);
public:
MYString();
MYString(const MYString& mystr);
MYString(const char*);
~MYString();
MYString& operator=(const MYString& rhs);
char& operator[](int index);
const char& operator[](int index) const;
int length() const;
int capacity() const;
const char* c_str();
MYString operator+(const MYString& rhs);
int operator==(const MYString& rhs);
int operator>(const MYString& rhs);
int operator<(const MYString& rhs);
};
Here is some relevant function defintions
MYString::MYString(const MYString& rhs){
*this = rhs;
}
MYString& MYString::operator=(const MYString& rhs){
if (this == &rhs){
return *this;
}
clear();
cap = rhs.cap;
end = rhs.end;
str = new char[cap];
for (int i = 0; i < end; i++){
str[i] = rhs[i];
}
str[end] = '\0';
return *this;
}
MYString::~MYString(){
clear();
}
void MYString::clear(){
cap = 0;
end = 0;
delete[] str;
str = nullptr;
}
The main method is as follows:
#include "MYString.h"
#include <iostream>
#include <vector>
#include <fstream>
using namespace std;
int main(){
ifstream input;
input.open("file.txt");
if (input.fail()){
cout << "File error" << endl;
}
MYString s;
vector<MYString> v;
int count = 0;
v.push_back(MYString());
int index = 0;
while (input >> s){
if (count == 2){
v.push_back(MYString());
count = 0;
index++;
}
if (count < 2){
v[index] = v[index] + s;
count++;
}
}
for (MYString& str : v){
cout << str << endl;
}
Serious problem with your constructor:
MYString::MYString(const MYString& rhs){
*this = rhs;
}
Inside the body of your object, your data is not initialized, but you dereference this and assign to it. The data members contain garbage, including the pointer. In the assignment operator, you call clear, which calls delete[] on this garbage pointer. This is undefined behavior.
Implementing a constructor in terms of assignment is always wrong. Assignment is for replacing state of an initialized object with new state, while constructors provide initial state to uninitialized members. The object's lifetime has not even started UNTIL the constructor completes. Calling functions on an object whose life has not officially started is undefined behavior.
Your copy constructor needs to allocate memory and copy the data into it, similar to what you did in your assignment operator, but not cleaning up the old state first. Also, consider using strcpy instead of a loop to copy bytes. It will be faster.
I've been writing my own String class and I am not sure how to write operator+ correctly considering I could pass rvalues into it.I think I should have the following 3 non-member functions
String operator+(String &&lhs, String &&rhs);
String operator+(String& lhs,String&&rhs);
String operator+(String&&lhs,String&rhs);
However I am not sure how to implement them. Any help would be appreciated.
First, make sure to define copy and move constructors in your String class:
class String
{
private:
char *m_data;
std::size_t m_length;
...
public:
String();
String(const String &src);
String(String &&src);
~String();
...
};
String::String() :
m_data(nullptr),
m_length(0)
{
}
String(const String &src) :
m_data(new char[src.m_length+1]),
m_length(src.m_length)
{
std::copy_n(src.m_data, m_length, m_data);
m_data[m_length] = 0;
}
String(String &&src) :
m_data(nullptr),
m_length(0)
{
std::swap(m_data, src.m_data);
std::swap(m_length, src.m_length);
}
String::~String()
{
delete[] m_data;
}
Then define operator+ and operator+= for the class:
class String
{
public:
...
String& operator+=(const String &rhs);
...
friend String operator+(String lhs, const String &rhs)
{
lhs += rhs;
return lhs;
}
};
String& String::operator+=(const String &rhs)
{
String tmp;
tmp.m_length = m_length + rhs.m_length;
tmp.m_data = new char[tmp.m_length+1];
std:copy_n(m_data, m_length, tmp.m_data);
std:copy_n(rhs.m_data, rhs.m_length, tmp.m_data + m_length);
tmp.m_data[tmp.m_length] = 0;
std::swap(m_data, tmp.m_data);
std::swap(m_length, tmp.m_length);
return *this;
}
By taking a const String & as input on the right side, that will handle both lvalue and rvalue inputs.
For operator+, the left-hand side is taken by value so the compiler can decide the best constructor to use based on whether the input is an lvalue (copy) or rvalue (move).
Alternatively, you can implement it to take const String & on the left side so it still handles lvalues and rvalues, but then you have to implement it similar to how operator+= is implemented to avoid the extra allocation of copying lhs before concatenating onto it:
friend String operator+(const String &lhs, const String &rhs)
{
/*
String tmp(lhs);
tmp += rhs;
return tmp;
*/
String tmp;
tmp.m_length = lhs.m_length + rhs.m_length;
tmp.m_data = new char[tmp.m_length+1];
std:copy_n(lhs.m_data, lhs.m_length, tmp.m_data);
std:copy_n(rhs.m_data, rhs.m_length, tmp.m_data + lhs.m_length);
tmp.m_data[tmp.m_length] = 0;
return tmp;
}
Either way, you should also define a conversion constructor and operator+ for const char * input as well:
class String
{
public:
...
String(const char *src);
...
friend String operator+(const char *lhs, const String &rhs)
{
return String(lhs) + rhs;
/* or:
std::size_t len = std::strlen(lhs);
String tmp;
tmp.m_length = len + rhs.m_length;
tmp.m_data = new char[tmp.m_length+1];
std:copy_n(lhs, len, tmp.m_data);
std:copy_n(rhs.m_data, rhs.m_length, tmp.m_data + len);
tmp.m_data[tmp.m_length] = 0;
return tmp;
*/
}
...
};
String::String(const char *src) :
m_data(nullptr),
m_length(std::strlen(src))
{
m_data = new char[m_length+1];
std::copy_n(src, m_length, m_data);
m_data[m_length] = 0;
}
This will allow concatenating String objects with string literals (String + "literal", "literal" + String, String += "literal", etc).
See operator overloading on cppreference.com for more details.
The way I usually do it is like this:
class foo
{
...
public:
...
foo&& operator +(foo const & other) &&;
foo&& operator +(foo && other) const &;
foo&& operator +(foo && other) &&;
foo operator +(foo const & other) const &;
};
Not sure if Microsoft supports this but this is a good way to do this in more recent standards. Try clang if msvc wont let you.
The advantages of doing it this way are that you get very fine levels of control over the method used. These 4 operations can also be defined outside of the class if needed. But you'll always want 4 for the 4 possibilities of r-value/l-value combinations.
Also, you generally want to qualify l-values as const to indicate that they are not modified.
Simply defining a copy/move constructor is not usually an efficient solution to this problem. You will need a good understanding of how rvalue references work to implement this efficiently.
I try to overload relational operator in my own String class. This how the class look like: I store the string inside data which is an array.
class MyString
{
public:
//default constructor, create an empty string
MyString()
{
};
//create a string containing n copies of c
MyString(size_t n, char c) :data(new char[n]), data_length(n)
{
for (size_t i = 0; i < n; i++)
{
data[i] = c;
}
};
//create a string from a null-terminated array
MyString(const char *cp):data(new char[std::strlen(cp)]), data_length(std::strlen(cp))
{
//std::copy(cp, cp + std::strlen(cp), data);
std::copy(cp, cp + std::strlen(cp), stdext::checked_array_iterator<char*>(data, data_length));
}
//destructor
~MyString()
{
//free the array
delete[] data;
};
//relational operators
bool operator==(const char* lhs, const char* rhs)
{
}
private:
size_t data_length;
char* data;
};
Compiler says that cant overload more than 2 args so I understand that lhs must be implicit like this:
bool operator==(const char* rhs)
{
}
but, my problem is:
Supposing that I compare two strings which sit inside a null-terminated array how can I compare them in something like a loop?
Just started learning C++ recently and I'm attempting to make my own string class from scratch. I'm currently working on concatenating strings by overloading += and + operators. After reading this article, basic-rules-of-operator-overloading, I have come up with the following implementation;
String & String::operator+=(const String &o)
{
char * newBuffer = new char[this->size() + o.size() - 1];
//copy over 'this' string to the new buffer
int index = 0;
while (this->at(index) != 0x0)
{
*(newBuffer + index) = this->at(index);
index++;
}
//copy over the param string into the buffer with the offset
//of the length of the string that's allready in the buffer
int secondIndex = 0;
while (o.at(secondIndex) != 0x0)
{
*(newBuffer + index + secondIndex) = o.at(secondIndex);
secondIndex++;
}
//include the trailing null
*(newBuffer + index + secondIndex) = 0x0;
//de-allocate the current string buffer and replace it with newBuffer
delete[] this->s;
this->s = newBuffer;
this->n = index + secondIndex;
return *this;
}
inline String operator+(String lhs, const String &rhs)
{
lhs += rhs;
return lhs;
}
However, the compiler will not recognise the + overload! It does work if I place the function in the main test file (where I am calling the method) but not if I place it in my String.cpp file where all my other methods are located.
Here is my String.h file if you need it;
#include <iostream>
class String
{
public:
String(const char * s);
String(const String &o);
int size() const;
char at(int i) const;
String &operator+=(const String &o);
private:
char * s;
int n;
//needs to be a friend function defined OUTSIDE of the class as when using
//ostream << String you do not have access to the ostream so they can't be
//member operators
friend std::ostream & operator<<(std::ostream &os, const String &o);
};
Thanks for any help!
(also, anything you think I can improve on in regards to my implementation would be graciously received)
Well everyone already explained, so it should be as simple as just adding the forward declaration to the end of your .h file like this:
#include <iostream>
class String
{
public:
String(const char * s);
String(const String &o);
int size() const;
char at(int i) const;
String &operator+=(const String &o);
private:
char * s;
int n;
//needs to be a friend function defined OUTSIDE of the class as when using
//ostream << String you do not have access to the ostream so they can't be
//member operators
friend std::ostream & operator<<(std::ostream &os, const String &o);
};
//forward declaration
String operator+(String lhs, const String &rhs);
The forward declaration just tells the compiler to look for a function with that signature. When it doesn't find it in your current .cpp file it looks up on the other .cpp files. I hope this helps!
I have a test class of my to make my own string functions. I have a problem with the copy destructor.
I have 2 strings: s1 and s2.
I call the function s3 = s1 + s2;
It first calls the operator+ function and when it's finished it calls the destructor. Because of this the string object in the operator= function is empty. How can I fix this?
Destructor:
String::~String() {
if (this->str)
delete[] str;
str = NULL;
len = 0;
}
Copy Constructor:
String::String(const String& string) {
this->len = string.len;
if(string.str) {
this->str = new char[string.len+1];
strcpy(this->str,string.str);
} else {
this->str = 0;
}
}
operator=:
String & String::operator= (const String& string) {
if(this == & string)
return *this;
delete [] str;
this->len = string.len;
if(string.str) {
this->str = new char[this->len];
strcpy(this->str,string.str);
} else {
this->str = 0;
}
return *this;
}
operator+:
String& operator+(const String& string1 ,const String& string2)
{
String s;
s.len = string1.len + string2.len;
s.str = new char[string1.len + string2.len+1];
strcpy(s.str,string1.str);
strcat(s.str,string2.str);
return s;
}
operator+ should not return a local variable by reference.
Change the return type of operator+ to String. Ie, make the signature:
String operator+( String const& lhs, String const& rhs )
You probably also want to write a "move constructor" for your String class: String( String&& other ) if you are writing your code in C++11.
A simple move constructor:
String::String( String&& other ): len(other.len), str(other.str) {
other.len = 0;
other.str = nullptr;
}
This isn't required, because the copy in the return statement of your operator+ will probably be "elided" by your compiler under non-trivial optimization levels, but still good practice.
It's calling the Destructor because String s is going out of scope in your operator+ overload. Your operator+ overload needs to be returning a copy instead of a reference.
Therefore you should change your operator+ to
String operator+(const String& string1, const String& string2)
Yeah i got your problem
The thing is when you are returning a reference to a temp object from + operator function and then you are assigning this to other object in main So here = overloaded function gets called in to which you are passing a reference to an object that no longer exists
So either you can return a copy from + operator function
or
you can pass a copy in the = overlaoded function