Mystring Class

very good guidelines on operator overload

http://www.cs.caltech.edu/courses/cs11/material/cpp/donnie/cpp-ops.html

the guidelines for the assignment operator = are:

1. Take a const-reference for the argument (the right-hand side of the assignment).
2. Return a reference to the left-hand side, to support safe and reasonable operator chaining. (Do this by returning *this.)
3. Check for self-assignment, by comparing the pointers (this to &rhs).
4. release memory of myself before assign;

part 1.
Also, you will notice that a reference is returned by the assignment operator. (the return type is not void). This is to allow operator chaining. You typically see it with primitive types, like this:

int a, b, c, d, e;

a = b = c = d = e = 42;

This is interpreted by the compiler as:

a = (b = (c = (d = (e = 42))));

In other words, assignment is right-associative. The last assignment operation is evaluated first, and is propagated leftward through the series of assignments. Specifically:

* e = 42 assigns 42 to e, then returns e as the result
* The value of e is then assigned to d, and then d is returned as the result
* The value of d is then assigned to c, and then c is returned as the result
* etc.

Now, in order to support operator chaining, the assignment operator must return some value instead of void. The value that should be returned is a reference to the left-hand side of the assignment.

part 2.
Notice that the returned reference is not declared const. This can be a bit confusing, because it allows you to write crazy stuff like this:

MyClass a, b, c;

(a = b) = c; // What??

At first glance, you might want to prevent situations like this, by having operator= return a const reference. However, statements like this will work with primitive types. And, even worse, some tools actually rely on this behavior. Therefore, it is important to return a non-const reference from your operator=. The rule of thumb is, "If it's good enough for ints, it's good enough for user-defined data-types."

the guidelines for Compound Assignment Operators += -= *=

just write your compound assignment operator implementation on itself, and return *this at the end, just like for the regular assignment operator.

But, you will notice that the operator returns a reference, and a non-const one at that. This is so you can do things like this:

MyClass mc;

(mc += 5) += 3;

**Don't ask me why somebody would want to do this, but just like the normal assignment operator, this is allowed by the primitive data types.
like :

int x;

(x+=5)+=3;

Our user-defined datatypes should match the same general characteristics of the primitive data types when it comes to operators, to make sure that everything works as expected.**

the guidelines for the binary arithmetic operators are:

1. Implement the compound assignment operators from scratch, and then define the binary arithmetic operators in terms of the corresponding compound assignment operators.
2. Declare a local variable of the self type, operate on it, return the local variable;
3. Return a const instance, to prevent worthless and confusing assignment operations that shouldn't be allowed.

const MyClass MyClass::operator+(const MyClass &other) const {
    MyClass result = *this;     // Make a copy of myself.  Same as MyClass result(*this);
    result += other;            // Use += to add other to the copy.
    return result;              // All done!
  }

You will notice that the + operator returns a const instance, not a const reference. This is so that people can't write strange statements like this:

MyClass a, b, c;
  ...
  (a + b) = c;   // Wuh...?

the sample

#ifndef MYSTRING_H
#define MYSTRING_H

#include <iostream>
using namespace std;

class MyString {
public:
//Constructors, Copy, and Destrctors

MyString();
MyString(const char *inString);
MyString & operator=(const MyString & right); // Notice return by reference, and not friend class
MyString(const MyString &right); //notice no return type

virtual ~MyString(); //destructor

//input/output
int length()const;

friend ostream& operator«(ostream& out,const MyString &outString); notice return by reference, outString is const;
friend istream& operator»(istream& in,MyString &inString);
notice inSTring is not const

void read(istream& inString,char delimit);

//Overloaded brackets
char& operator[](int index);
char operator[](int index)const;

friend const MyString operator+(const MyString other); notice this has to be friend,all return by const value
MyString& operator+=( const MyString &right);
notice return by reference

friend bool operator<(const MyString &left, const MyString &right); notice this has to be friend,
friend bool operator<=(const MyString &left, const MyString &right);
notice this has to be friend,
friend bool operator>=(const MyString &left, const MyString &right);notice this has to be friend,
friend bool operator>(const MyString &left, const MyString &right);
notice this has to be friend,
friend bool operator==(const MyString &left, const MyString &right);notice this has to be friend,
friend bool operator!=(const MyString &left, const MyString &right);
notice this has to be friend,

private:
char * string;

};

#endif

MyString::MyString()
{
string = new char[1];
string[0] = '\0';
}

MyString::MyString(const char *inString)
{
string = new char[strlen(inString)+1];
strcpy(string,inString);
}

MyString::~MyString()
{
delete []string;
}

ostream& operator«(ostream &out,const MyString &outString)
{
out«outString.string;
return out; //notice the return of out
}

MyString & MyString:: operator=(const MyString & right) return by reference
{
if (this != &right)
{
delete []string;
string = new char[strlen(right.string)+1];
notice assignment is not friend function, even though it access private member variable
strcpy(string,right.string);
}
return *this;
}

MyString::MyString(const MyString &right)
{
delete [] string;
string = new char[strlen(right.string)+1];
strcpy(string,right.string);
}

char& MyString::operator[](int index)
{
assert(index>=0 && index < strlen(string));
return string[index];
}

char MyString::operator[](int index)const
{
assert(index>=0 && index < strlen(string));
return string[index];
}

istream& operator»(istream& in,MyString &inString)
{

char temp[128];
delete [] inString.string;
in » temp;
inString.string = new char [ strlen( temp ) + 1 ];
strcpy(inString.string, temp);

return in;
}

void MyString::read(istream &inString,char delimit)
{
char temp[128];
delete []string;
inString.getline(temp,127,delimit);
inString.string = new char [ strlen( temp ) + 1 ];
strcpy(string,temp);
}

const MyString operator+(const MyString other) //notice it return const value
{
MyString temp;

temp.string = new char[strlen(this->string)+strlen(other.string)+1];

strcpy(temp.string,this->string);
strcat(temp.string,right.string);

return temp;
}

MyString& MyString :: operator+=(const MyString &right)
{
*this = *this+right;
return *this;
}

int MyString::length()const
{
return strlen(string);
}

bool operator<(const MyString &left, const MyString &right)
{
return strcmp(left.string,right.string) < 0;
}

bool operator<=(const MyString &left, const MyString &right)
{
return strcmp(left.string,right.string) <= 1;
}

bool operator>(const MyString &left, const MyString &right)
{
return strcmp(left.string,right.string) > 0;
}

bool operator>=(const MyString &left, const MyString &right)
{
return strcmp(left.string,right.string) >= 1;
}

bool operator==(const MyString &left, const MyString &right)
{
return strcmp(left.string,right.string) == 1;
}

bool operator!=(const MyString &left, const MyString &right)
{
return strcmp(left.string,right.string) != 1;
}

/*

  • ---——
  • These functions are designed to help you test your MyString objects,
  • as well as show the client usage of the class.

*

  • The BasicTest function builds an array of strings using various
  • constructor options and prints them out. It also uses the String
  • stream operations to read some strings from a data file.

*

  • The RelationTest function checks out the basic relational operations
  • (==, !=, <, etc) on Strings and char *s.

*

  • The ConcatTest functions checks the overloaded + and += operators that
  • do string concatenation.

*

  • The CopyTest tries out the copy constructor and assignment operators
  • to make sure they do a true deep copy.

*

  • Although not exhaustive, these tests will help you to exercise the basic
  • functionality of the class and show you how a client might use it.

*

  • While you are developing your MyString class, you might find it
  • easier to comment out functions you are ready for, so that you don't
  • get lots of compile/link complaints.

*/

#include "mystring.h"
#include <fstream>
#include <cctype> // for toupper()
#include <string> // for strchr(), strstr(), etc.
#include <cassert>
#include <iostream>
using namespace std;

bool eof(istream& in);
void BasicTest();
void RelationTest();
void ConcatTest();
void CopyTest();
MyString AppendTest(const MyString& ref, MyString val);

int main()
{
BasicTest();
RelationTest();
ConcatTest();
CopyTest();

}

bool eof(istream& in)
{
char ch;
in » ch;
in.putback(ch);
return !in;
}

void BasicTest()
{
cout « "- Testing basic String creation & printing" « endl;

const MyString strs[] =
{MyString("Wow"), MyString("C++ is neat!"),
MyString(""), MyString("a-z")};

for (int i = 0; i < 4; i++){
cout « "string [" « i «"] = " « strs[i] « endl;
}

cout « endl « "- Now reading MyStrings from file" « endl;

cout « endl « "- first, word by word" « endl;
ifstream in("string.data");
assert(in);
while (!eof(in)) {
MyString s; // creates an empty string
if (in.peek() == '#') { // peek at char, comments start with #
in.ignore(128, '\n'); // skip this line, it's a comment
} else {
in » s;
cout « "Read string = " « s « endl;
}
}
in.close();

cout « endl « "- now, line by line" « endl;
ifstream in2("string.data");
assert(in2);
while (!eof(in2)) {
MyString s; // creates an empty string
if (in2.peek() == '#') { // peek at char, comments start with #
in2.ignore(128, '\n'); // skip this line, it's a comment
} else {
s.read(in2, '\n');
cout « "Read string = " « s « endl;
}
}
in2.close();

cout « endl « "- Testing access to characters (using const)" «
endl;
const MyString s("abcdefghijklmnopqsrtuvwxyz");
cout « "Whole string is " « s « endl;
cout « "now char by char: ";
for (int i = 0; i < s.length(); i++){
cout « s[i];
}

cout « endl « "- Testing access to characters (using non-const)"
« endl;
MyString s2("abcdefghijklmnopqsrtuvwxyz");
cout « "Start with " « s2;
for (int i = 0; i < s.length(); i++){
s2[i] = toupper(s2[i]);
}
cout « " and convert to " « s2 « endl;
}

void RelationTest()
{
cout « "\n- Testing relational operators between MyStrings\n";

const MyString strs[] =
{MyString("app"), MyString("apple"), MyString(""),
MyString("Banana"), MyString("Banana")};

for (int i = 0; i < 4; i++) {
cout « "Comparing " « strs[i] « " to " « strs[i+1] « endl;
cout « "\tIs left < right? " « (strs[i] < strs[i+1]) « endl;
cout « "\tIs left <= right? " « (strs[i] <= strs[i+1]) « endl;
cout « "\tIs left > right? " « (strs[i] > strs[i+1]) « endl;
cout « "\tIs left >= right? " « (strs[i] >= strs[i+1]) « endl;
cout « "\tDoes left == right? " « (strs[i] == strs[i+1]) « endl;
cout « "\tDoes left != right ? " « (strs[i] != strs[i+1]) « endl;
}

cout « "\n- Testing relations between MyStrings and char *\n";
MyString s("he");
const char *t = "hello";
cout « "Comparing " « s « " to " « t « endl;
cout « "\tIs left < right? " « (s < t) « endl;
cout « "\tIs left <= right? " « (s <= t) « endl;
cout « "\tIs left > right? " « (s > t) « endl;
cout « "\tIs left >= right? " « (s >= t) « endl;
cout « "\tDoes left == right? " « (s == t) « endl;
cout « "\tDoes left != right ? " « (s != t) « endl;

MyString u("wackity");
const char *v = "why";
cout « "Comparing " « v « " to " « u « endl;
cout « "\tIs left < right? " « (v < u) « endl;
cout « "\tIs left <= right? " « (v <= u) « endl;
cout « "\tIs left > right? " « (v > u) « endl;
cout « "\tIs left >= right? " « (v >= u) « endl;
cout « "\tDoes left == right? " « (v == u) « endl;
cout « "\tDoes left != right ? " « (v != u) « endl;

}

void ConcatTest()
{
cout « "\n- Testing concatentation on MyStrings\n";

const MyString s[] =
{MyString("outrageous"), MyString("milk"), MyString(""),
MyString("cow"), MyString("bell")};

for (int i = 0; i < 4; i++) {
cout « s[i] « " + " « s[i+1] « " = " « s[i] + s[i+1] « endl;
}

cout « "\n- Testing concatentation between MyString and char *\n";

const MyString a("abcde");
const char *b = "XYZ";
cout « a « " + " « b « " = " « a + b « endl;
cout « b « " + " « a « " = " « b + a « endl;

cout « "\n- Testing shorthand concat/assign on MyStrings\n";

MyString s2[] =
{MyString("who"), MyString("what"), MyString("WHEN"),
MyString("Where"), MyString("why")};

for (int i = 0; i < 4; i++) {
cout « s2[i] « " += " « s2[i+1] « " = ";
cout « (s2[i] += s2[i+1]) « endl;
}

cout « "\n- Testing shorthand concat/assign using char *\n";
MyString u("I love ");
const char *v = "programming";
cout « u « " += " « v « " = ";
cout « (u += v) « endl;
}

MyString AppendTest(const MyString& ref, MyString val)
{
val[0] = 'B';
return val + ref;
}

void CopyTest()
{
cout « "\n- Testing copy constructor and operator= on MyStrings\n";

MyString orig("cake");

MyString copy(orig); // invoke copy constructor

copy[0] = 'f'; // change first letter of the *copy*
cout « "original is " « orig « ", copy is " « copy « endl;

MyString copy2; // makes an empty string

copy2 = orig; // invoke operator=
copy2[0] = 'f'; // change first letter of the *copy*
cout « "original is " « orig « ", copy is " « copy2 « endl;

copy2 = "Copy Cat";
copy2 = copy2; // copy onto self and see what happens
cout « "after self assignment, copy is " « copy2 « endl;

cout « "Testing pass & return MyStrings by value and ref" « endl;
MyString val = "winky";
MyString sum = AppendTest("Boo", val);
cout « "after calling Append, sum is " « sum « endl;
cout « "val is " « val « endl;
val = sum;
cout « "after assign, val is " « val « endl;

}

a very good c++ test

http://warp.povusers.org/c++test/index.html

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License