Concatenating two dynamic string arrays? - c++

template <typename Object>
class Vector1 {
public:
explicit Vector1(const Object & value = Object()) : size_{0} {
array_ = new Object{value};
size_++;
}
Vector1(const Vector1 & rhs) : size_{rhs.size_} { //copy constructor
array_ = new Object[size_];
for (int i = 0; i < size_; i++) {
array_[i] = rhs.array_[i];
}
}
Vector1 & operator=(const Vector1 & rhs) { //copy assignment operator
array_ = new Object[rhs.size_];
if (this != &rhs) {
size_ = rhs.size_;
for (int i = 0; i < size_; i++) {
array_[i] = rhs.array_[i];
}
}
return *this;
}
Vector1(Vector1 && rhs) : array_{rhs.array_}, size_{rhs.size_} { //move constructor
rhs.array_ = nullptr;
rhs.size_ = 0;
}
Vector1 & operator=(Vector1 && rhs) { //move assignment operator
if (this != &rhs) {
std::swap(size_, rhs.size_);
std::swap(array_, rhs.array_);
}
return *this;
}
void print(ostream & out) const {
for (int i = 0; i < size_; i++) {
out << array_[i] << " ";
}
}
void ReadVector1() {
int count = 0;
cout << "Enter a size: ";
cin >> size_;
array_ = new Object[size_];
for (int i = 0; i < size_; i++) {
cout << "Enter element " << count + 1 << ": ";
cin >> array_[i];
count++;
}
}
size_t Size() const {
return size_;
}
**Vector1 operator+=(Vector1 & rhs) {
size_t combosize = size_ + rhs.size_;
Object combo[combosize];
int count = 0, rhscount = 0;
for (int i = 0; i < combosize; i++) {
if (i % 2 == 0) {
combo[i] = array_[count];
count++;
}
else {
combo[i] = rhs.array_[rhscount];
rhscount++;
}
}
std::swap(combosize, rhs.size_);
std::swap(combo, rhs.array_);
return *this;
}
Vector1 operator+(const Vector1 & rhs) const {
Vector1 temp(*this);
temp += rhs;
return temp;
}**
~Vector1() { //destructor
delete[] array_;
}
private:
size_t size_;
Object *array_;
};
template <typename Object>
ostream & operator<<(ostream & out, const Vector1<Object> & rhs) {
rhs.print(out);
return out;
}
int main(int argc, char **argv) {
Vector1<string> a, b;
a.ReadVector1(); //user provides input for Vector1 a
cout << a << endl;
b.ReadVector1(); //user provides input for Vector1 b
cout << b << endl;
cout << a + b << endl; //concatenates the two Vector1s
Vector1<string> d = a + b;
cout << d;
return 0;
}
The above is my code (thus far). What I'm trying to accomplish is to concatenate a's dynamic array with b's dynamic array (I cannot use vectors or any STLs, this is meant to be a rudimentary imitation of a vector).
Example:
The user inputs a size of 2 for a and inputs "Hello" and "World".
The user inputs a size of 2 for b and inputs "Goodbye" and "World".
The output should be "Hello World Goodbye World".
I highlighted what I think the problem is, which is the overloading of + and the += operators. My reasoning is that I create a new array, fill it up with the value's from a and b, swap the values, and then return the supposed concatenation.
My reasoning may not seem sound because frankly, I'm quite confused on how to proceed with this.

There are a few issues with your code concerning operator +=.
First, operator += should return a reference to the current object, i.e. return *this;. It shouldn't return a brand new Vector1 object.
Second, this line:
size_t combosize = size_ + rhs.size_;
Object combo[combosize];
is not valid ANSI C++, since arrays must be declared with a compile-time expression to denote the number of entries. Since combosize is a runtime value, it cannot be used.
You are probably using the GCC compiler, where there is a Variable Length Array extension, but again, this is an extension and is not really part of the C++ language.
Also, you are missing some constructors that would make writing operator += much easier. The constructor for Vector1 that you're lacking is this one:
Vector1::Vector1(size_t num) : array_(new Object[num]), size_(num) {}
This constructor just constructs a Vector1 with num entries.
Given the above, to fix the issues:
Vector1& operator+=(const Vector1 & rhs)
{
// create a temporary vector
Vector1 temp(size_ + rhs.size_);
// copy elements to temp array
for (int i = 0; i < size_; i++)
temp.array_[i] = array_[i];
// copy elements from rhs to temp array
int j = 0;
for (int i = size_; i < size_ + rhs.size_; i++, j++)
temp.array_[i] = rhs.array_[j];
// assign and return
*this = temp;
return *this;
}
Vector1 operator+(const Vector1 & rhs) const
{
Vector1 temp(*this);
temp += rhs;
return temp;
}
Note that in operator += we just create a temporary Vector1, fill it up with the values from *this and the passed in Vector1, and assign it to the current object. The *this = temp; line requires a working assignment operator. See the next section below.
The other issue is your Vector1::operator= is incorrect. It does not deallocate the memory allocated to _array previously, thus you have a memory leak.
The easiest way to fix this is to use the copy / swap you used in the operator=(Vector1 &&):
Vector1 & operator=(const Vector1 & rhs)
{
Vector1 temp(rhs);
std::swap(size_, temp.size_);
std::swap(array_, temp.array_);
return *this;
}

Related

Destructor crashes when working with pointers in Visual Studio

When I'm using a pointer to an instance to this class, for some reason it crashes when it comes to the destructor. It works when I'm not using pointers
The weirdest part is that it works in VC Code but not in Visual Studio.
Polynomial.h
class Polynomial {
private:
int32_t* coeffs;
uint32_t deg;
public:
Polynomial(int32_t* setCoeffs, uint32_t setDeg) {
deg = setDeg;
while (setCoeffs[deg] == 0 && deg > 0) deg--;
coeffs = new int32_t[deg + 1]();
for (uint32_t i = 0; i <= deg; i++) {
coeffs[i] = setCoeffs[i];
}
}
~Polynomial () {
delete[] coeffs;
}
std::string str() {
std::string out = "";
for (uint32_t i = deg; i > 0; i--) {
if (coeffs[i] == 0) continue;
if (coeffs[i] > 0 && i != deg) out += "+";
if (coeffs[i] != 1)
out += std::to_string(coeffs[i]);
if (i == 1) out += "x";
else if (i > 1) out += "x^" + std::to_string(i);
}
if (coeffs[0] != 0) {
if (coeffs[0] > 0) out += "+";
out += std::to_string(coeffs[0]);
}
return out;
}
friend std::ostream& operator<<(std::ostream& out, Polynomial p) {
out << p.str();
return out;
}
};
main.cpp
#include <iostream>
#include "Polynomial.h"
int main(){
int32_t* coeffs = new int32_t[3]();
coeffs[0] = 5;
coeffs[1] = 4;
coeffs[2] = 3;
Polynomial* p = new Polynomial(coeffs, 2);
std::cout << (*p);
delete p;
return 0;
}
I'm guessing it's something to do with the Visual Studio properties but I don't know what to change.
Your overload of the << operator takes its argument by value; so, in the line, std::cout << (*p); a copy of *p is made and, when the output is done, that copy is destroyed. However, as pointed out in the comments, you haven't implemented a proper copy constructor, so the compiler provides a default, which simply copies the data members' values (including the int32_t* coeffs pointer member).
So, when the output operation is done, and the copy is destroyed, the memory pointed-to by coeffs is deleted; then, when you come to delete the original, you are trying to delete that same memory a second time – hence the crash.
To fix the issue either: (a) implement proper copy and assignment constructors, which make real copies of the coeffs data; or (b) give the argument to your << operator by reference, as follows:
friend std::ostream& operator<<(std::ostream& out, Polynomial& p) { // Pass "p" BY REFERENCE
out << p.str();
return out;
}
Of course, making the '(b)' change doesn't (shouldn't) mean that you can't also implement '(a)'. You should really do both; here's a likely implementation of the copy constuctor:
Polynomial(const Polynomial& rhs) {
deg = rhs.deg;
coeffs = new int32_t[deg + 1];
for (uint32_t i = 0; i <= deg; i++) {
coeffs[i] = rhs.coeffs[i];
}
}

Remove an object from an array of pointers

I have an array of pointers that point to Building objects that look like this:
class Building {
int id ;
int year;
double price;
char* adress;
};
What I'm trying to do is to remove objects from the array via the id using operator-, but I don't really know how to do so. My list class looks like this:
class List {
Building* buildingList[10];
static int buildingNr;
public:
List operator-(int id) {
bool exists = false;
int index;
for(int i = 0; i < buildingNr; i++)
if (buildingList[i]->getId() == id) {
exists = true;
index = i;
cout << "Building " << index << " was deleted." << endl;
}
if (exists) {
delete buildingList[index];
buildingList[index] = buildingList[buildingNr];
buildingList[buildingNr] = NULL;
buildingNr--;
}
else throw - 1;
}
};
int List::buildingNr = 0;
I know that there are far easier ways of doing this, like using std::vector, but this is an assignment and I have to do it using the overloaded operator- and an array of pointers, and the Building class has to look like that.
I also have the operator+ which adds an element to the array, and that works fine.
Your operator- is not implemented correctly. Although its loop to find an object is fine, removing that object is broken. You are not updating the array correctly, as you need to shift down ALL of the array elements after the found index, which you are not doing.
Also, make sure your List class implements the Rule of 3/5/0 correctly. The return value of operator- is supposed to return a new List object, so a copy has to be made. You should not be modifying the this object at all in operator- (that is the job of operator-= to do; see What are the basic rules and idioms for operator overloading?).
Also, the buildingNr member should not be static. That prevents you from using multiple List objects at a time.
Try something more like this instead:
#include <algorithm>
class List {
Building* buildingList[10];
int buildingNr;
public:
List() : buildingNr(0) {}
List(const List &src) : buildingNr(src.buildingNr) {
for(int i = 0; i < buildingNr; ++i) {
buildingList[i] = new Building;
buildingList[i]->id = src.buildingList[i]->id;
buildingList[i]->year = src.buildingList[i]->year;
buildingList[i]->price = src.buildingList[i]->price;
buildingList[i]->adress = new char[strlen(src.buildingList[i]->adress)+1);
strcpy(buildingList[i]->adress, src.buildingList[i]->adress);
}
}
~List() {
for(int i = 0; i < buildingNr; ++i) {
delete[] buildingList[i]->adress;
delete buildingList[i];
}
}
List& operator=(const List &rhs) {
if (this != &rhs) {
List tmp(rhs);
std::swap_ranges(buildingList, buildingList+10, tmp.buildingList);
std::swap(buildingNr, tmp.buildingNr);
}
return *this;
}
List& operator-=(int id) {
for(int i = 0; i < buildingNr; ++i) {
if (buildingList[i]->getId() == id) {
delete buildingList[i];
for(int j = i + 1; j < buildingNr; ++j) {
buildingList[j - 1] = buildingList[j];
}
buildingList[buildingNr] = NULL;
--buildingNr;
cout << "Building " << i << " was deleted." << endl;
return *this;
}
}
throw -1;
}
...
};
friend List operator-(List lhs, int id) {
lhs -= id;
return lhs;
}

How can I pass a int value to my member function operator += to insert int values into a object list in c++?

I'm very new to c++ and have issues doing what seems like a simple task. I was given a main function to test and I have to create a class that correctly corresponds to the int main. Below is the class I've made so far. There's a lot more to it than what I've provided because I'm having issues starting off the basic parts of the class.
The task is to create a class that makes object lists of ints and can be manipulated with other functions that come later. For now, I am trying to set up the lists and inserting int n values.
#include<iostream>
#include<string>
#include<cmath>
#define DEFAULT 8
#define EMPTY -1
using namespace std;
// Your class specification goes here <<<<<<<<<<<<<<
class Myclass
{
private:
int *a = NULL;
int end = EMPTY;
int size;
public:
Myclass(){} //default constructor
Myclass(Myclass const &other);//copy constructor
Myclass(Myclass &&other);//move constructor
~Myclass() {delete [] a;} //destructor
Myclass& operator=(const Myclass& other); //copy assignment
Myclass& operator=(Myclass&& other); //move assignment
Myclass& operator+=(const Myclass &other);
Myclass& operator+(Myclassconst &other); // overload +
friend ostream& operator<<(ostream &os, const Myclass& other);
};
this in my class so far and below is the definitions.
Myclass::Myclass(Myclass const &other) // copy constructor
{
end = other.end;
a = new int [DEFAULT];
for (int i = 0; i <= end; i++)
a[i] = other.a[i];
}
Myclass::Myclass(Myclass &&other) //move constructor
{
a = new int [DEFAULT];
for (int i = 0; i <= end; i++)
a[i] = other.a[i];
}
Myclass& Myclass::operator=(const Myclass& other) //overload =
{
if (this == &other) // Check for self assignment
return *this;
end = other.end;
delete [] a;
a = new int [DEFAULT];
for (int i = 0; i <= end; i++)
a[i] = other.a[i];
return *this;
}
Myclass& Myclass::operator=(Myclass&& other)
{
if(this != &other)
{
delete[] a;
a = new int [DEFAULT];
for (int i = 0; i <= end; i++)
{
a[i] = other.a[i];
}
}
return *this;
}
Myclass& Myclass::operator+(Myclass const &other) //overload +
{
Myclass answer;
int index = 0;
for (int i = 0; i < this->end; i++)
answer.a[index++] = this->a[i];
for (int i = 0; i <= other.end; i++)
answer.a[index++] = other.a[i];
return answer;
}
Myclass& Myclass::operator+=(const Myclass&other)
{
Myclass answer;
int index = 0;
for (int i = 0; i < this->end; i++)
answer.a[index++] = this->a[i];
return answer;
}
ostream& operator<<(ostream &os, const Myclass& other)
{
for (int i = 0; i <= other.end; i++)
os << other.a[i] << ", ";
return os;
}
My main issue is getting the += operator to work correctly as I keep getting the error about adding Myclass and an int together. My += function does not seem correct either but im not sure how to even correct it. I've commented out a2 and a3 because I'm not sure how to deal with the true or false of these objects either.
Please help in any way or send me in a direction of information Id really appreciate it.
Your operator+= doesn't accept an int as input, only another Alist, so a1 += n; will not work. You need to add another operator+= for that purpose.
Also, your array management is completely wrong in your constructors and assignment operators. Your copy constructor and copy assignment operator are not allocating enough memory to copy the entire other list if it has more than DEFAULT integers. And your move constructor and move assignment operator are not actually moving anything at all.
And there are other issues with all of your operators in general. Your concatenation operators are not appending to the array correctly. And the non-compound operators have the wrong return type. See What are the basic rules and idioms for operator overloading?
Try something more like this:
Alist.h
#ifndef ALIST_H
#define ALIST_H
#include <iostream>
class Alist
{
private:
int *a = nullptr;
int size = 0;
int capacity = 0;
public:
Alist() = default; //default constructor
Alist(int initialCap);
Alist(Alist const &other); //copy constructor
Alist(Alist &&other); //move constructor
~Alist(); //destructor
void swap(Alist &other);
Alist& operator=(Alist other); //copy+move assignment
Alist& operator+=(const Alist &other);
Alist& operator+=(int value);
Alist operator+(const Alist &other) const; // overload +
Alist operator+(int value) const;
//int& operator[](int index);
friend std::ostream& operator<<(std::ostream &os, const Alist& list);
};
void swap(Alist &a1, Alist &a2);
#endif
Alist.cpp
#include "Alist.h"
#include <utility>
#define GROWTH 8 // must be a power of 2!
Alist::Alist(int initialCap)
: Alist()
{
if (initialCap > 0) {
capacity = (initialCap + (GROWTH-1)) & ~(GROWTH-1);
a = new int [capacity];
}
}
Alist::Alist(const Alist &other)
: Alist(other.size)
{
size = other.size;
for (int i = 0; i < size; ++i) {
a[i] = other.a[i];
}
}
Alist::Alist(Alist &&other)
: Alist()
{
other.swap(*this);
}
Alist::~Alist()
{
delete [] a;
}
void Alist::swap(Alist &other)
{
std::swap(capacity, other.capacity);
std::swap(size, other.size);
std::swap(a, other.a);
}
Alist& Alist::operator=(Alist other)
{
other.swap(*this);
return *this;
}
Alist Alist::operator+(const Alist &other) const
{
Alist answer(size + other.size);
answer += *this;
answer += other;
return answer;
}
Alist Alist::operator+(int value) const
{
Alist answer(size + 1);
answer += *this;
answer += value;
return answer;
}
Alist& Alist::operator+=(const Alist &other)
{
if ((size + other.size) > capacity)
{
Alist tmp(size + other.size);
tmp += *this;
tmp.swap(*this);
}
for (int i = 0; i < other.size; ++i) {
a[size++] = other.a[i];
}
return *this;
}
Alist& Alist::operator+=(int value)
{
if ((size + 1) > capacity)
{
Alist tmp(size + 1);
tmp += *this;
tmp.swap(*this);
}
a[size++] = value;
return *this;
}
//int& Alist::operator[](int index)
//{
// return a[index];
//}
std::ostream& operator<<(std::ostream &os, const Alist& list)
{
for (int i = 0; i < list.size; i++)
os << list.a[i] << ", ";
return os;
}
void swap(Alist &a1, Alist &a2)
{
a1.swap(a2);
}

Overloading addition operator (adding a string to an object using a function)

I'm creating a dynamic array class and I'm new to c++. I'm having trouble overloading the addition operator to add a string to an object. When I do try to add a string, nothing shows up on the compile screen. I also added my copy constructor, destructor, overloaded assignment operator, and overloaded ostream operator just in case any of those were the issue. Thank you so much for the help!!
DynamicStringArray::~DynamicStringArray()
{
delete[] dynamic_Array;
dynamic_Array = NULL;
}
DynamicStringArray::DynamicStringArray(const DynamicStringArray& first)
{
size = first.returns_Size();
dynamic_Array = new string[size];
for (int n = 0; n < size; n++)
{
dynamic_Array[n] = first.get_Entry(n);
}
}
void DynamicStringArray::operator =(const DynamicStringArray& first)
{
this->size = first.returns_Size();
this->dynamic_Array = new string[size];
for (int i = 0; i < this->size; i++)
{
this->dynamic_Array[i] = first.get_Entry(i);
}
}
ostream& operator <<(ostream& out, const DynamicStringArray& first) //nonmember requires 2 arguments
{
for (int i = 0; i < first.size; i++)
{
out << first.dynamic_Array[i] << endl;
}
return out;
}
void DynamicStringArray::add_Entry(string a)
{
string* Temp_Array = dynamic_Array; //old array
dynamic_Array = new string[size + 1]; //new array
for (int i= 0; i < size; i++) //copy old string values to temp array
{
dynamic_Array[i] = Temp_Array[i];
}
dynamic_Array[size] = a; //puts string a into last position of new array
delete[]Temp_Array; //free memory space
size++;
}
DynamicStringArray DynamicStringArray::operator +(const string& a)
{
DynamicStringArray added;
added.add_Entry(a);
return added;
}
int main()
{
DynamicStringArray fav_Foods;
fav_Foods.add_Entry("pasta");
fav_Foods.add_Entry("sushi");
fav_Foods + "Burgers";
cout << fav_Foods << endl;
}
DynamicStringArray DynamicStringArray::operator +(const string& a)
{
DynamicStringArray added;
added.add_Entry(a);
return added;
}
Why do you think you need to create a new DynamicStringArray added?
Simply call add_Entry(a) on the current instance. Also, operator+() should return a reference to the instance it is called upon:
DynamicStringArray& DynamicStringArray::operator+(string const &a)
{
add_Entry(a);
return *this;
}

To clarify concept of relational operator overloading (Debugging help needed)

Let me tell you guys I am beginner to c++.
For educational and learning purpose I have created my own string class named MyString. As per instructions from my instructor I am not allowed to use standard library functions to compare two strings.
MyString class contains char type pointer and integer type variable that holds length of string i.e:
class MyString{
char *str; int len;
public:
MyString(){
len = 1;
str = new char[len];
str[len - 1] = '\0';
}
MyString(char *p){
int count = 0;
for (int i = 0; p[i] != '\0'; i++){
count++;
}
len = count;
str = new char[len];
for (int i = 0; i < len; i++){
str[i] = p[i];
}
}
int length(){
return len;
}
bool operator < (MyString obj){
char temp;
if (len < obj.len){ return true; }
if (len>obj.len){ return false; }
if (this->len == obj.len){
for (int i = 0; i < len; i++){
if (this->str[i] < obj.str[i])
{
return true;
}
}
}
}
bool operator > (MyString obj) {
if (len > obj.len) {
return true;
}
if (len<obj.len) {
return false;
}
if (this->len == obj.len)
{
for (int i = 0; i < this->len; i++) {
if (this->str[i] > obj.str[i]) {
return true;
}
}
}
}
bool operator == (MyString obj) {
int count = 0;
if (this->len == obj.len){
for (int i = 0; i < this->len; i++) {
if (this->str[i] == obj.str[i]) {
count++;
}
}
if (count == len) {
return true;
}
}
}
char & operator[](int i) {
return str[i];
}
};
Here is main
int main()
{
char arr1[30], arr2[30];
cout << "Enter first MyString: ";
cin.get(arr1, 30);
cin.ignore();
cout << "Enter second MyString: ";
cin.get(arr2, 30);
MyString s1(arr1); //parametrized constructor
MyString s2(arr2);
cout << "Length of s1:" << s1.length() << endl;
cout << "Length of s2:" << s2.length() << endl;
if (s1<s2) // < operator overloaded
cout << "s1 < s2" << endl;
else if (s1>s2) // > operator overloaded
cout << "s1 > s2" << endl;
else if (s1 == s2) // == operator overloaded
cout << "s1 == s2" << endl;
return 0;
}
My algo for comparing two strings is:
i).First check length of two strings if len(length of s1) is less than obj.len(length of s2) than it returns true.
ii).if lengths are equal, compare for each element of s1 char array with s2 char array.Even if one of element of s1 char array is less than that s2 char array element (in ASCII) than return true otherwise return false.
The problems is whenever program executes, on console it shows "s1< s2" no matter if two strings passed are equal .
You're trying to write simple classes that allocate resources. This is a very important skill to have. What you've written so far has some good code, but also a lot of mistakes. The main errors are
Wrong algorithm for operator<. In your code "hello" < "goodbye" which is incorrect
Missing return statements.
Lack of destructor, so your class leaks memory.
Once you add a destructor you will also need a copy constructor and copy assignment operator otherwise your code will crash by freeing the same memory twice, this is known as the rule of three. Google it as it's perhaps the most important piece of C++ advice you'll read.
Lack of awareness of const correctness.
Lack of awareness of pass by reference.
Less than ideal signatures for your overloaded operators.
Missing some important methods on your class
Missing a few implementation tricks.
Putting all that together here an implementation of your class following the rules you've been given, with a few comments as well
class MyString {
char *str; int len;
public:
// default constructor should create a empty string, i.e. a zero length string
MyString() {
len = 0;
str = new char[len];
}
// contents of p are not changed so make it const
MyString(const char *p) {
int count = 0;
for (int i = 0; p[i] != '\0'; i++){
count++;
}
len = count;
str = new char[len];
for (int i = 0; i < len; i++){
str[i] = p[i];
}
}
// destructor, frees memory
~MyString() {
delete[] str;
}
// copy constructor, similar to the above except it starts from a MyString
MyString(const MyString& o) {
len = o.len;
str = new char[len];
for (int i = 0; i < len; i++){
str[i] = o.str[i];
}
}
// swap method, efficient exchange of two strings
void swap(MyString& o)
{
int t1 = o.len;
o.len = len;
len = t1;
char* t2 = o.str;
o.str = str;
str = t2;
}
// assignment operator, uses copy and swap idiom
MyString& operator=(MyString o) {
swap(o);
return *this;
}
// length does not modify the string, so it should be decalred const
int length() const {
return len;
}
char& operator[](int i) {
return str[i];
}
// need a const version of operator[] as well, otherwise you won't be able to do [] on a const string
char operator[](int i) const {
return str[i];
}
};
// operator< should be a function not a class method. This is the only way to get
// C++ to treat the two arguments symmetrically. For instance with your version
// "abc" < str is not legal, but str < "abc" is. This oddity is because C++ will
// not implicitly create a MyString object to call a MyString method but it will implicitly
// create a MyString object to pass a parameter. So if operator< is a function you will
// get implicit creation of MyString objects on either side and both "abc" < str and
// str < "abc" are legal.
// You also should pass to parameters by const reference to avoid unnecessary
// copying of MyString objects.
// Finally this uses the conventional algorithm for operator<
bool operator<(const MyString& lhs, const MyString& rhs) {
for (int i = 0; ; ++i)
{
if (i == rhs.length())
return false;
if (i == lhs.length())
return true;
if (lhs[i] > rhs[i])
return false;
if (lhs[i] < rhs[i])
return true;
}
}
// This is the easy way to write operator>
bool operator>(const MyString& lhs, const MyString& rhs) {
return rhs < lhs;
}
// This is the easy way to write operator<=
bool operator<=(const MyString& lhs, const MyString& rhs) {
return !(rhs < lhs);
}
// This is the easy way to write operator>=
bool operator>=(const MyString& lhs, const MyString& rhs) {
return !(lhs < rhs);
}
// operator== is a function not a method for exactly the same reasons as operator<
bool operator==(const MyString& lhs, const MyString& rhs) {
if (lhs.length() != rhs.length())
return false;
for (int i = 0; i < lhs.length(); ++i)
if (lhs[i] != rhs[i])
return false;
return true;
}
// this is the easy way to write operator!=
bool operator!=(const MyString& lhs, const MyString& rhs) {
return !(lhs == rhs);
}
There are countless issues with your code, so I'll provide an improved, commented implementation for you:
class MyString
{
char* str;
unsigned int len; // strings can't have negative length; using unsigned reflects this better
// even better: use size_t; this is the type for the concrete system
// able to cover any allocatable memory size
public:
MyString()
: str(new char[1]), len(1) // prefer initializer list
{
str[0] = 0; // does not matter if you use 0 or '\0', just my personal preference...
}
MyString(char const* p)
// you make a copy of, so have a const pointer (you can pass both const and non-const to)
{
for(len = 1; p[len] != 0; ++len);
// ^ make sure to copy the terminating null character as well!
str = new char[len];
for (unsigned int i = 0; i < len; i++)
{
str[i] = p[i];
}
// or use memcpy, if allowed
}
// OK, above, you allocated memory, so you need to free it again:
~MyString() // if you want to be able to inherit from, it should be virtual;
// strings, though, most likely should not be inherited from...
{
delete[] str;
}
// C++ creates a default copy constructor; this one, however, just copies all members by value
// i. e. copies the POINTER str, but not the memory pointed to, i. e. does not perform a deep copy
// which is what you need, however, to avoid double deletion:
MyString(MyString const& other)
: str(new char[other.len]), len(other.len)
{
for (unsigned int i = 0; i < len; i++)
{
str[i] = other.str[i];
}
}
// similar for assignment; I'm using copy and swap idiom to reduce code duplication here:
MyString& operator=(MyString other)
{
swap(other);
return *this;
}
void swap(MyString& other)
{
char* str = this->str;
unsigned int len = this->len;
this->str = other.str;
this->len = other.len;
other.str = str;
other.len = len;
}
unsigned int length() const
// ^^^^^ allows to retrieve length from a
// const MyString as well!
{
return len;
}
// fine, you can change the character within the string
char& operator[](unsigned int i)
{
return str[i];
}
// but what, if you have a const MyString???
// solution:
char operator[](unsigned int i) const
// ^^^^^
{
return str[i];
}
// you could alternatively return a const reference,
// but char is just too small that a reference would be worth the effort
// additionally: a reference could have the const casted away by user
// which is not possible by returning a copy, so we gain a little of safety as well...
bool operator<(MyString const& other) const
// ^^^^^^
// we don't need a copy and don't want a copy(it would just costs runtime and memory for nothing)!
// -> pass by const reference
// additionally, we want to be able to do comparison on const this as well (see length)
//
{
// have you noticed that you have one and the same code in all of your comparison operators???
// only the comparison itself changes lets have it just a little bit cleverer:
return compare(other) < 0;
}
bool operator>(MyString const& other) const
{
return compare(other) > 0;
}
bool operator==(MyString const& other) const
{
return compare(other) == 0;
}
// and for completeness:
bool operator<=(MyString const& other) const
{
return compare(other) <= 0;
}
bool operator>=(MyString const& other) const
{
return compare(other) >= 0;
}
bool operator!=(MyString const& other) const
{
return compare(other) != 0;
}
// the upcoming space ship operator (<=>) will simplify this, well, OK, but for now, we don't have it yet...
int compare(MyString const& other) const
{
// I decided to compare "abcd" smaller than "xyz" intentionally
// for demonstration purposes; just place your length checks
// back to get your original comparison again
unsigned int pos = 0;
// EDIT: "stealing" john's implementation, as superior to
// mine (with minor adaptions) ...
for (unsigned int pos = 0; ; ++pos)
{
///////////////////////////////////////////////////
// if you have your original length checks placed back above,
// just have the following check instead of the active one:
// if(pos == len) return 0;
if (pos == len)
{
return pos == other.len ? 0 : -pos - 1;
}
if (pos == other.len)
{
return pos + 1;
}
///////////////////////////////////////////////////
if(str[pos] < other.str[pos])
{
return -pos - 1;
}
if(str[pos] > other.str[pos])
{
return pos + 1;
}
}
return 0;
}
// WARNING: above code has yet an issue! I wanted to allow (for demonstration)
// to return positional information so that we not only see the result of comparison
// but can conclude to at WHERE the two strings differ (but need 1-based offset for to
// distinguish from equality, thus addition/subtraction of 1);
// however, on VERY large strings (longer than std::numeric_limits<int>::max()/-[...]::min()), we get
// signed integer overflow on the implicit cast, which is undefined behaviour
// you might want to check against the limits and in case of overflow, just return the limits
// (leaving this to you...)
// alternative: just return -1, 0, +1, the issue is gone as well...
};
OK, you now could just copy this code, strip the comments and present it as "your" solution. This is not what I intended this answer for! Take your time and read my comments carefully – you can learn quite a bit from...
Finally: there is yet another improvement possible: Before C++11, you could only copy data, if passing objects by value. Since C++, we additionally can move data from one object into another one – however, the type needs to support move semantics. You can do so by additionally providing a move constructor and copy assignment:
MyString(MyString&& other)
: str(nullptr), len(0)
{
// delete[]'ing nullptr (in other!) is OK, so we don't need
// to add a check to destructor and just can swap again...
swap(other);
}
MyString& operator=(MyString&& other)
{
// and AGAIN, we just can swap;
// whatever this contained, other will clean it up...
swap(other);
return *this;
}
You might be interested in further reading:
rule of three
rule of five
copy and swap idiom
In your function,
bool operator < (MyString obj){
I see no way to return false at the end!
It only returns true if it reach the 3rd if.
In addition, as others mentioned, the length does not imply the comparison in the way that you implemented.
Just a comment: Your code is prone to memory leak. It allocates but does not release the memory.
This code has lots of mistakes:
No copy constructor and copy is created
Missing destructor saves the day (memory leak, but thanks to that you do not have a crash for point 1)
len = 1 for empty string (default constructor).
MyString(char *p) do not add terminating character
not using const MyString &obj (unneeded copies).
missing return values at the end of various branches of methods
bool operator < (const MyString &obj) {
if (len < obj.len) {
return true;
}
if (len>obj.len) {
return false;
}
for (int i = 0; i < len; i++) {
if (this->str[i] != obj.str[i]) {
return this->str[i] < obj.str[i];
}
}
return false;
}