Implement relational operator in string class c++ - c++

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?

Related

Correct way to write operator+ using rvalues

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.

How do you get a + overload to become recognised in another file C++?

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!

overload array operator for mystring class

I need help figuring out how to overload the array operator for a MyString class that I have to create. I already have everything else figured out, but the arrays are giving me trouble, for some reason.
Here is my header file:
#ifndef MYSTRING_H
#define MYSTRING_H
#include <iostream>
#include <cstring> // For string library functions
#include <cstdlib> // For exit() function
using namespace std;
// MyString class: An abstract data type for handling strings
class MyString
{
private:
char *str;
int len;
public:
// Default constructor.
MyString()
{
str = 0;
len = 0;
}
// Convert and copy constructors.
MyString(char *);
MyString(MyString &);
// Destructor.
~MyString()
{
if (len != 0)
delete [] str;
str = 0;
len = 0;
}
// Various member functions and operators.
int length() { return len; }
char *getValue() { return str; };
MyString operator+=(MyString &);
MyString operator+=(const char *);
MyString operator=(MyString &);
MyString operator=(const char *);
bool operator==(MyString &);
bool operator==(const char *);
bool operator!=(MyString &);
bool operator!=(const char *);
bool operator>(MyString &);
bool operator>(const char *);
bool operator<(MyString &);
bool operator<(const char *);
bool operator>=(MyString &);
bool operator>=(const char*);
bool operator<=(MyString &);
bool operator<=(const char *);
MyString operator [](MyString *);
// Overload insertion and extraction operators.
friend ostream &operator<<(ostream &, MyString &);
friend istream &operator>>(istream &, MyString &);
};
#endif
What would the body look like for MyString::operator []?
MyString MyString::operator [](MyString *)
{
... what goes here
}
The syntax for using the array operator with an object of the given class is:
MyString s("Test");
char c = s[0];
The argument to the function is an integral value.
Hence, the operator needs to be declared as:
// The non-const version allows you to change the
// content using the array operator.
char& operator [](size_t index);
// The nconst version allows you to just get the
// content using the array operator.
char operator [](size_t index) const;
MyString MyString::operator [](MyString *)
That's not how you should typically use a subscript operator.
What do you expect when you are using the [] operator? By the way you declared it, you are using a string pointer as argument, and receiving a string as return.
Usually, you pass an index type (commonly an unsigned-integer like size_t) and return the character at that position. If that's what you want, you should do something along these lines:
char& MyString::operator [](size_t position)
{
// some error handling
return str[position];
}
char MyString::operator [](size_t position) const { /* ... */ }
For overall guidelines on overloading operators, take a look at What are the basic rules and idioms for operator overloading?.
Also, I would point out that your destructor is a bit odd:
if (len != 0)
delete [] str;
str = 0;
len = 0;
Your indentation level suggests that you expect everything to happen inside the if statement, but only the first one will. That is not particularly dangerous in this case, because only the delete would suffice.
There is no problem in deleteing a null pointer, and str and len will be destroyed shortly after, so you don't have to bother resetting them.

c++ User defined conversion - implicit conversion [duplicate]

This question already has answers here:
What is The Rule of Three?
(8 answers)
Closed 7 years ago.
I have a question about user defined conversion.
class String {
char* m_data;
public:
String(): m_data(NULL) {}
String(const char* cstr): m_data(new char[strlen(cstr)+1]) {
strcpy(m_data, cstr);
}
~String() {
delete[] m_data;
}
String& operator=(const char* cstr) {
delete[] m_data;
m_data = new char[strlen(cstr)+1];
strcpy(m_data, cstr);
return *this;
}
operator const char*() const {
return m_data;
}
};
While this works:
int main(int argc, char** argv) {
String a;
String b;
a = "aaa";
b = (const char *)a;
return 0;
}
This does not:
int main(int argc, char** argv) {
String a;
String b;
a = "aaa";
b = a;
return 0;
}
I get double free or corruption runtime error. Valgrind says something about invalid delete.
Why do I have to explicitly typecast it? I thought it would work this way with explicit operator const char*(). Am I doing something wrong?
You forgot to define copy assignment operator:
String& operator=(const String& other) {
if(this != &other) {
char* new_data = new char[strlen(other.m_data)+1];
strcpy(new_data, other.m_data);
delete[] m_data;
m_data = new_data;
}
return *this;
}
Because of that your compiler had to define "default" copy assignment operator, which simple assigns all fields of "other" to a current object:
String& operator=(const String& other) {
m_data = other.m_data;
return *this;
}
So you have two pointers to same m_data in a and b and on the exit from main, delete[] will be called twice.

Error in my str class

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.