Home | Projects | Notes > C++ Programming > Operator Overloading
C++ allows the programmer to overload most operators to work with user-defined classes. This is intended to make code more readable and writable by allowing the use of familiar operators in a familiar manner, but with our own classes and objects.
What is operator overloading?
Overloading the assignment operator (=
)
Copy semantics
Move semantics
Overloading operators as member functions
Overloading operators as global functions
Overloading stream insertion (<<
) and extraction operators (>>
)
Using traditional operators such as +
, =
, *
, etc. with user-defined types.
Allows user-defined types to behave and feel similar to built-in types
Can make code more readable and writable
Not done automatically (except for the assignment operator)
They must be explicitly defined.
The only operator that the compiler provides a default implementation for is the assignment operator (=
). This is because the compiler must be able to assign one object to another. All the other operators that can be overloaded must be explicitly defined by the programmer.
Suppose we have a Number
class that models any number.
Using functions:
xxxxxxxxxx
11Number result = multiply(add(a, b), divide(c, d));
Using member functions:
xxxxxxxxxx
11Number result = (a.add(b)).multiply(c.divide(d));
These statements are very unreadable and very complicated to write. Why can't we use the basic arithmetic symbols we have been using since elementary school now?
Using overloaded operators:
xxxxxxxxxx
11Number result = (a + b) * (c / d);
Now it looks and feels and behaves like the built-in C++ types.
Operator overloading is syntactic sugar. Behind the scenes, we're still calling functions.
The majority of C++'s operators can be overloaded.
The following operators CANNOT be overloaded:
::
(scope resolution operator)
:?
(conditional operator)
.*
(pointer to member operator)
.
(dot operator)
sizeof
Remember, just because an operator can be overloaded doesn't mean you should. Don't overload it unless it makes sense and makes your code more usable, more readable and more writable.
We can make the operator mean anything we want. We want to make sure that when we do overload operators it makes sense and the users of the class know about it.
Precedence and Associativity CANNOT be changed.
"-arity" CANNOT be changed. (i.e., can't make the division operator "unary")
Can't overload operators for primitive type (e.g., int
, double
, etc.)
Can't create new operators
[]
, ()
, ->
, and the assignment operator (=
) MUST be declared as member functions.
Other operators can be declared as member functions or global functions.
Operator Overloading used on C++ Built-In Types:
int
xxxxxxxxxx
31a = b + c
2a < b
3std::cout << a
double
xxxxxxxxxx
31a = b + c
2a < b
3std::cout << a
long
xxxxxxxxxx
31a = b + c
2a < b
3std::cout << a
Operator Overloading used on User-Defined Types:
std::string
xxxxxxxxxx
31s1 = s2 + s3
2s1 < s2
3std::cout << s1
Player
xxxxxxxxxx
31p1 < p2
2p1 == p2
3std::cout << p1
Mystring
xxxxxxxxxx
41s1 = s2 + s3
2s1 < s2
3s1 == s2
4std::cout << s1
Mystring
class declaration: (Incomplete)
See Complete Mystring
Class section for the complete class implementation.
xxxxxxxxxx
191
2
3
4class Mystring
5{
6private:
7 char *str; // pointer to a char[] that holds a C-style string
8
9public:
10 Mystring(); // default constructor
11 Mystring(const char *s); // parameterized constructor
12 Mystring(const Mystring &source); // copy constructor
13 ~Mystring(); // destructor
14 void display() const;
15 int get_length() const; // getter
16 const char* get_str() const; // getter
17};
18
19// MYSTRING_H
Mystring
class implementation: (Incomplete)
See Complete Mystring
Class section for the complete class implementation.
xxxxxxxxxx
601// behind the scenes, Mystring class uses c-string library
2
3
4
5// default constructor
6Mystring::Mystring()
7 :str(nullptr)
8{
9 str = new char[1];
10 *str = '\0';
11}
12
13// parameterized constructor
14Mystring::Mystring(const char *s)
15 :str(nullptr)
16{
17 // if an empty string has been passed, create an empty string ""
18 if (s == nullptr)
19 {
20 str = new char[1];
21 *str = '\0';
22 }
23 else
24 {
25 str = new char[std::strlen(s) + 1];
26 std::strcpy(str, s);
27 }
28}
29
30// copy constructor
31Mystring::Mystring(const Mystring &source)
32 : str(nullptr)
33{
34 str = new char[std::strlen(source.str) + 1];
35 std::strcpy(str, source.str);
36}
37
38// destructor
39Mystring::~Mystring()
40{
41 delete[] str;
42}
43
44// display method
45void Mystring::display() const
46{
47 std::cout << str << ":" << get_length() << std::endl;
48}
49
50// length getter
51int Mystring::get_length() const
52{
53 return std::strlen(str);
54}
55
56// string getter
57const char* Mystring::get_str() const
58{
59 return str;
60}
Test driver:
See Complete Mystring
Class section for the complete class implementation.
xxxxxxxxxx
151
2
3
4int main(int argc, char *argv[])
5{
6 Mystring empty; // default constructor
7 Mystring larry("Larry"); // overloaded constructor
8 Mystring stooge(larry); // copy constructor
9
10 empty.display();
11 larry.display();
12 stooge.display();
13
14 return 0;
15}
xxxxxxxxxx
31:0
2Larry:5
3Larry:5
When you don't provide a user-defined copy assignment operator, C++ will generate a default assignment operator used for assigning one object to another.
xxxxxxxxxx
51Mystring s1("Frank");
2Mystring s2 = s1; // NOT assignment because s2 hasn't been created yet!
3 // same as Mystring s2(s1);
4
5s2 = s1; // assignment since s2 has already been created and initialized
Don't confuse "initialization" with "assignment". Initialization is done by constructors when we create new objects.
Default behavior is memberwise assignment (shallow copy).
If we have raw pointer data member, we must deep copy.
Copy assignment operator works with L-value references.
Overloading the copy assignment operator (deep copy):
xxxxxxxxxx
11Type &Type::operator=(const Type &rhs);
Example:
xxxxxxxxxx
51Mystring& Mystring::operator=(const Mystring &rhs); // method name is 'operator='
2
3s2 = s1; // we write this
4s2.operator=(s1); // compiler will convert 's2 = s1' into this
5 // 'operator=' method is called
Returning the reference is important because we don't want to make an extra copy of what we are returning and we want to allow chain assignments such as
s1 = s2 = s3
.
The object on the left-hand side of an assignment statement is referred to by the this
pointer. The object on the right-hand side is being passed into the method. The semantics is that the object on the LHS is going to be overwritten by the object on the RHS.
First, we need to deallocate anything it refers to on the heap. (If we don't do this, we'll orphan this memory and end up with a memory leak.)
Then we need to allocate the space in the LHS object for the RHS side object's data, and then we finally copy the data over to the left side from the right side.
Overloaded copy assignment operator for Mystring
class:
xxxxxxxxxx
131Mystring& Mystring::operator=(const Mystring &rhs)
2{
3 // check for self assignment
4 if (this == &rhs)
5 return *this; // if so, return current object
6
7 delete[] str; // deallocate storage for 'this->str' since we are overwriting it
8 // delete[] this->str; also works
9 str = new char[std::strlen(rhs.str) + 1]; // allocate storage for the deep copy
10 std::strcpy(str, rhs.str); // perform the copy
11
12 return *this; // return the LHS object by reference to allow chain assignment
13}
Test driver:
See Complete Mystring
Class secction for the complete class implementation.
xxxxxxxxxx
131
2
3
4int main(int argc, char *argv[])
5{
6 Mystring a("Hello"); // parameterized constructor
7 Mystring b; // default constructor
8 b = a; // copy assignment operator
9 // b.operator=(a)
10 b = "This is a test"; // b.operator=("This is a test");
11
12 return 0;
13}
In line
, the copy assignment operator will be called, a temporary object will be created and it will be assigned over. Once the assignment is done, the destructor will be called to destroy the temporary object.
You can choose to overload the move assignment operator.
C++ will use the copy assignment operator (copy assignment operator) by default, if necessary.
xxxxxxxxxx
41Mystring s1; // empty string
2s1 = Mystring("Frank"); // move assignment will be called since we are providing R-value reference
3 ----------------- -----------------
4 temporary object is created "Frank"
If we have raw pointer we should overload the move assignment operator for efficiency.
Move assignment operator works with R-value references. (Think temporary unnamed objects!)
Overloading the move assignment operator:
xxxxxxxxxx
41Type& Type::operator=(Type &&rhs);
2 --
3 to tell the compiler that the right-side object is an R-value
4 so the right-side value will be an R-value reference
Note that the RHS object CANNOT be const
since we'll be modifying that object when we move the data. (i.e., Nullifying the pointer)
Example:
xxxxxxxxxx
31Mystring& Mystring::operator=(Mystring &&rhs);
2s1 = Mystring("Joe"); // move operator= called
3s1 = "Frank" // move operator= called
xxxxxxxxxx
111Mystring &Mystring::operator=(Mystring &&rhs)
2{
3 // check for self assignment
4 if (this == &rhs) // self assignment
5 return *this; // return current object
6
7 delete[] str; // deallocate current storage
8 str = rhs.str; // steal the pointer (NOT a deep copy)
9 rhs.str = nullptr; // null out the rhs object (to prevent memory leak)
10 return *this; // return current object
11}
Very similar to the copy assignment operator except that with move assignment operator we are NOT doing the deep copy. Instead, we're simply stealing the pointer and then nulling out the RHS pointer. Much more efficient (less overhead) than copy assignment.
Test driver:
See Complete Mystring
Class secction for the complete class implementation.
xxxxxxxxxx
151
2
3
4int main(int argc, char *argv[])
5{
6 Mystring a("Hello"); // overloaded (parameterized) constructor
7 a = Mystring("Hola"); // overloaded constructor then move assignment
8 // - a temporary unnamed object gets created with "Hola"
9 // and destroyed after move assignment operation is done
10 a = "Bonjour" // overloaded constructor then move assignment
11 // - a temporary unnamed object gets created with
12 // "Bonjour" and destroyed after move assignment
13 // operation is done
14 return 0;
15}
If the move constructor, move assignment operator are not defined, this code will invoke copy constructor (or the copy assignment operator) instead.
C++ allows us to overload operators as
Member functions
Global non-member functions
Here, let's take a look at overloading operators as member functions.
Overloading unary operators as member functions (++
, --
, -
, !
):
Unary operators work on one operand.
xxxxxxxxxx
31ReturnType Type::operatorOp();
2 --
3 operator goes here!
In the case the we have to return a new object from the method, we'll return the new object by value.
Example:
xxxxxxxxxx
91Number Number::operator-() const;
2Number Number::operator++(); // pre-increment
3Number Number::operator++(int); // post-increment
4bool Number::operator!() const;
5
6Number n1(100);
7Number n2 = -n1; // n1.operator-()
8n2 = ++n1; // n1.operator++()
9n2 = n1++; // n1.operator++(int)
Notice that unary member functions have an empty parameter list. This is because the object we're working with is referred to by the
this
pointer. Also, the keywordint
is used in the parameters for the post-increment to differentiate it from the pre-increment.
Example:
xxxxxxxxxx
81Mystring jack1{"JACK"};
2Mystring jack2;
3
4jack1.display(); // JACK
5jack2 = -jack1; // jack1.operator-()
6
7jack1.display(); // JACK
8jack2.display(); // jack
Note what the negation operator
-
does when used with a string object.
Overloading binary operators as member functions (+
, -
, ==
, !=
, <
, >
, etc.):
xxxxxxxxxx
31ReturnType Type::operatorOp(const Type &rhs);
2 --
3 operator goes here!
Example:
xxxxxxxxxx
91Number Number::operator+(const Number &rhs) const;
2Number Number::operator-(const Number &rhs) const;
3bool Number::operator==(const Number &rhs) const;
4bool Number::operator<(const Number &rhs) const;
5
6Number n1(100), n2(200);
7Number n3 = n1 + n2; // n1.operator+(n2)
8n3 = n1 - n2; // n1.operator-(n2)
9if (n1 == n2) . . . // n1.operator==(n2)
Now we have a single parameter in the method parameter list. Of course the this
pointer points to the LHS operand.
Implementation of operator==
:
xxxxxxxxxx
91bool Mystring::operator==(const Mystring &rhs) const
2{
3 if (std::strcmp(str, rhs.str) == 0) // <cstring>
4 return true;
5 else
6 return false;
7}
8
9// if (s1 == s2) // s1 and s2 are Mystring objects
Implementation of operator-
: (make lowercase)
xxxxxxxxxx
101Mystring Mystring::operator-() const
2{
3 char *buff = new char[std::strlen(str) + 1]; // Allocate memory space
4 std::strcpy(buff, str); // Copy the string
5 for (size_t i = 0; std::strlen(buff); i++) // Make each character lowercase
6 buff[i] = std::tolower(buff[i]); // tolower() is in <ccpyte> header file
7 Mystring temp(buff); // Create a Mystring obj using lowercase string as the initializer
8 delete[] buff; // Delete the buffer created to prevent leak memory
9 return temp; // Return the newly created Mystring object
10}
Implementation of operator+
: (concatenation)
xxxxxxxxxx
101Mystring Mystring::operator+(const Mystring &rhs) const
2{
3 size_t buff_size = std::strlen(str) + std::strlen(rhs.str) + 1;
4 char *buff = new char[buff_size]; // Allocate large enough memory space for two strings
5 std::strcpy(buff, str);
6 std::strcat(buff, rhs.str);
7 Mystring temp(buff);
8 delete[] buff;
9 return temp;
10}
xxxxxxxxxx
101Mystring larry("Larry");
2Mystring moe("Moe");
3Mystring stooges(" is one of the three stooges");
4
5Mystring result = larry + stooges; // larry.operator+(stooges);
6result = moe + " is also a stooge"; // moe.operator+("is also a stooge");
7
8result = "Moe" + stooges; // Illegal! "Meo".operator+(stooges)
9 // -----
10 // not an obj of the class this fcn is called on
Notice that we can also use c-style strings on the right-hand side. This is because we have a mystring constructor that can construct mystring objects from a c-style string.
The only limitation to overloading operators as member functions is that the object on the left-hand side must be an object of the class you are using.
These are no longer member functions, so we do not have this
pointer referring to the object on the left-hand side.
Since we very often need access to private attributes in the objects, these non-member functions are often declared as friend functions of the class in many applications. (This isn't absolutely necessary since we can still use getter functions to access attribute values.)
When you have both the member and non-member versions of an overloaded operator at the same time, make sure that the cases in which they are applied are distinctive. (e.g., Applicable operand types are different, etc.) Otherwise, the compiler wouldn't know which one to use and would throw an error or warning upon encountering one.
Overloading unary operators as global functions(++
, --
, -
, !
):
xxxxxxxxxx
31ReturnType operatorOp(Type &obj)
2 --
3 operator goes here!
Example:
xxxxxxxxxx
91Number operator-(const Number &obj);
2Number operator++(Number &obj); // pre-increment
3Number operator++(Number &obj, int); // post-increment
4bool operator!(const Number &obj);
5
6Number n1(100);
7Number n2 = -n1; // operator-(n1)
8n2 = ++n1; // operator++(n1)
9n2 = n1++; // operator++(n1, int)
In the case of the unary operators, a single object is in the parameter list. In the case of the binary operators, two objects are in the parameter list.
Implementation of operator -
as a global function: (make lowercase)
It is assumed that this function has been declared as a friend to the Mystring
class.
xxxxxxxxxx
101Mystring operator-(const Mystring &obj)
2{
3 char *buff = new char[std::strlen(obj.str) + 1];
4 std::strcpy(buff, obj.str);
5 for (size_t i = 0; i < std::strlen(buff); i++)
6 buff[i] = std::tolower(buff[i]);
7 Mystring temp(buff);
8 delete[] buff;
9 return temp;
10}
You CANNOT have both the member and non-member versions of this overloaded operator at the same time. Or the compiler wouldn't know which one to use. You can only have one or the other.
Overloading binary operators as global functions (+
, -
, ==
, !=
, <
, >
, etc.):
xxxxxxxxxx
31ReturnType operatorOp(const Type &lhs, const Type &rhs);
2 --
3 operator goes here!
Example:
xxxxxxxxxx
91Number operator+(const Number &lhs, const Number &rhs);
2Number operator-(const Number &lhs, const Number &rhs);
3bool operator==(const Number &lhs, const Number &rhs);
4bool operator<(const Number &lhs, const Number &rhs);
5
6Number n1(100), n2(200);
7Number n3 = n1 + n2; // operator+(n1, n2)
8n3 = n1 - n2: // operator-(n1, n2)
9if (n1 == n2) ... // operator==(n1, n2)
Implementation of operator==
:
xxxxxxxxxx
71bool operator==(const Mystring &lhs, const Mystring &rhs)
2{
3 if (std::strcmp(lhs.str, rhs.str) == 0)
4 return true;
5 else
6 return false;
7}
If declared as a friend of
Mystring
, it can access a private attributestr
. Otherwise a getter function must be used.The code is almost the same as it was for the member version except that now it has an LHS object instead of the
this
pointer.
Implementation of operator+
(concatenation):
xxxxxxxxxx
181Mystring operator+(const Mystring &lhs, const Mystring &rhs)
2{
3 size_t buff_size = std::strlen(lhs.str) + std::strlen(rhs.str) + 1;
4 char *buff = new char[buff_size];
5 std::strcpy(buff, lhs.str);
6 std::strcat(buff, rhs.str);
7 Mystring temp(buff);
8 delete[] buff;
9 return temp;
10}
11Mystring larry("Larry");
12Mystring moe("Moe");
13Mystring stooges(" is one of the three stooges");
14
15Mystring result = larry + stooges; // larry.operator+(stooges);
16result = moe + " is also a stooge"; // moe.operator+("is also a stooge");
17
18result = "Moe" + stooges; // OK with non-member functions
Notice that now the very last line of code is legal with the global (non-member) overloading function.
Test driver:
xxxxxxxxxx
321
2
3
4
5using namespace std;
6
7int main(int argc, char *argv[])
8{
9 Mystring larry("Larry");
10 larry.display(); // Larry
11
12 larry = -larry;
13 larry.display(); // larry
14
15 cout << boolalpha << endl;
16 Mystring moe("Moe");
17 Mystring stooge = larry;
18
19 cout << (larry == moe) << endl; // false
20 cout << (larry == stooge) << endl; // true
21
22 //Mystring stooges = larry + "Moe";
23 Mystring stooges = "Larry" + moe; // Legal with non-member function
24 stooges.display(); // LarryMoe
25
26 Mystring two_stooges = moe + " " + Larry;
27 two_stooges.display(); // Moe Larry
28 Mystring three_stooges = moe + " " + larry + " " + "Curly";
29 three_stooges.display(); // Moe larry Curly
30
31 return 0;
32}
<<
, >>
)This will allow us to insert/extract our Mystring
objects to/from streams. (This makes our classes feel and behave more like a built-in C++ type.)
Doesn't make sense to implement as member functions.
Overloading operators as member functions requires left operand to be a user-defined class but this is not the way insertion/extraction operators are normally used.
xxxxxxxxxx
21Mystring jack;
2jack << cout; // Hun? Not consistent with how << is used with C++ built-in types
xxxxxxxxxx
21Mystring jack;
2cout << jack; // This is the way << is used with C++ built-in types
Makes more sense to overload these operators as global functions!
Overloading stream insertion operator:
xxxxxxxxxx
61std::stream& operator<<(std::ostream &os, const Mystring &obj)
2{
3 os << obj.str; // if friend function
4 //os << obj.get_str(); // if not friend function, then must use the public getter function
5 return os;
6}
Return a reference to the
ostream
(i.e., output stream reference) so we can keep inserting. Don't returnostream
by value! (This will incur unnecessary copying of the stream.)
Overloading stream insertion operator:
xxxxxxxxxx
81std::istream& operator>>(std::istream &is, Mystring &obj)
2{
3 char *buff = new char[1000];
4 is >> buff;
5 obj = Mystring(buff); // if copy or move assignment are predefined
6 delete[] buffer;
7 return is;
8}
We want to modify the passed object so this function should NOT be
const
.Update the object passed in.
Return a reference to the
istream
so we can do chain insert.Depending on the data we want to read, we can get the data from the input stream and either store it locally or store it directly in the object.
Test driver:
xxxxxxxxxx
231
2
3
4using namespace std;
5
6int main(int argc, char *argv[])
7{
8 Mystring larry("Larry");
9 Mystring moe("Moe");
10 Mystring curly;
11
12 cout << "Enter the third stooge's first name: ";
13 cin >> curly;
14
15 cout << "The three stooges are " << larry << " " << moe << " " << curly << endl;
16
17 cout << "\nEnter the three stooges names separated by a space: ";
18 cin >> larry >> moe >> curly;
19
20 cout << "The three stooges are " << larry << " " << moe << " " << curly << endl;
21
22 return 0;
23}
Mystring
Class==
, -
, +
oerators are overloaded as both member and non-member functions. As this can cause some ambiguity issues, it would be safe to test (or comment out) one group at a time.
Mystring
class declaration:
xxxxxxxxxx
341
2
3
4class Mystring
5{
6 friend bool operator==(const Mystring &lhs, const Mystring &rhs);
7 friend Mystring operator-(const Mystring &obj);
8 friend Mystring operator+(const Mystring &lhs, const Mystring &rhs);
9 friend std::ostream& operator<<(std::ostream &os, const Mystring &rhs);
10 friend std::istream& operator>>(std::istream &is, Mystring &rhs);
11
12private:
13 char *str; // pointer to a char[] that holds a C-style string
14
15public:
16 Mystring(); // default constructor
17 Mystring(const char *s); // parameterized constructor
18 Mystring(const Mystring &source); // copy constructor
19 Mystring(Mystring &&source); // move constructor
20 ~Mystring(); // destructor
21
22 Mystring& operator=(const Mystring &rhs); // copy assignment operator overloading
23 Mystring& operator=(Mystring &&rhs); // move assignment operator overloading
24
25 Mystring operator-() const; // make lowercase
26 Mystring operator+(const Mystring &rhs) const; // concatenate
27 bool operator==(const Mystring &rhs) const; // equality operator
28
29 void display() const;
30 int get_length() const; // getter
31 const char* get_str() const; // getter
32};
33
34// MYSTRING_H
Mystring
class implementation:
xxxxxxxxxx
1731// behind the scenes, Mystring class uses c-string library
2
3
4
5// default constructor
6Mystring::Mystring()
7 :str(nullptr)
8{
9 str = new char[1];
10 *str = '\0';
11}
12
13// parameterized constructor
14Mystring::Mystring(const char *s)
15 :str(nullptr)
16{
17 // if an empty string has been passed, create an empty string ""
18 if (s == nullptr)
19 {
20 str = new char[1];
21 *str = '\0';
22 }
23 else
24 {
25 str = new char[std::strlen(s) + 1];
26 std::strcpy(str, s);
27 }
28}
29
30// copy constructor
31Mystring::Mystring(const Mystring &source)
32 : str(nullptr)
33{
34 str = new char[std::strlen(source.str) + 1];
35 std::strcpy(str, source.str);
36}
37
38// move constructor
39Mystring::Mystring(Mystring &&source)
40 :str(source.str) // stealing the pointer of the source object
41{
42 source.str = nullptr;
43}
44
45// destructor
46Mystring::~Mystring()
47{
48 delete[] str;
49}
50
51// copy assignment operator
52Mystring& Mystring::operator=(const Mystring &rhs)
53{
54 // check for self assignment
55 if (this == &rhs)
56 return *this; // if so, return current object
57
58 delete[] str; // deallocate storage for 'this->str' since we are overwriting it
59 // delete[] this->str; also works
60 str = new char[std::strlen(rhs.str) + 1]; // allocate storage for the deep copy
61 std::strcpy(str, rhs.str); // perform the copy
62
63 return *this; // return the LHS object by reference to allow chain assignment
64}
65
66// move assignment operator
67Mystring& Mystring::operator=(Mystring &&rhs)
68{
69 // check for self assignment
70 if (this == &rhs) // self assignment
71 return *this; // return current object
72
73 delete[] str; // deallocate current storage
74 str = rhs.str; // steal the pointer (NOT a deep copy)
75 rhs.str = nullptr; // null out the rhs object (to prevent memory leak)
76 return *this; // return current object
77}
78
79// equality
80bool Mystring::operator==(const Mystring &rhs) const
81{
82 return (std::strcmp(str, rhs.str) == 0);
83}
84
85// make lowercase
86Mystring Mystring::operator-() const
87{
88 char *buff = new char[std::strlen(str) + 1];
89 std::strcpy(buff, str);
90 for (size_t i = 0; std::strlen(buff); i++)
91 buff[i] = std::tolower(buff[i]);
92 Mystring temp(buff);
93 delete[] buff;
94 return temp;
95}
96
97// concatenate
98Mystring Mystring::operator+(const Mystring &rhs) const
99{
100 char *buff = new char[std::strlen(str) + std::strlen(rhs.str) + 1];
101 std::strcpy(buff, str);
102 std::strcat(buff, rhs.str);
103 Mystring temp(buff);
104 delete[] buff;
105 return temp;
106}
107
108// display method
109void Mystring::display() const
110{
111 std::cout << str << ":" << get_length() << std::endl;
112}
113
114// length getter
115int Mystring::get_length() const
116{
117 return std::strlen(str);
118}
119
120// string getter
121const char* Mystring::get_str() const
122{
123 return str;
124}
125
126// equality (global)
127bool operator==(const Mystring &lhs, const Mystring &rhs)
128{
129 return (std::strcmp(lhs.str, rhs.str) == 0);
130
131}
132
133// make lowercase
134Mystring operator-(const Mystring &obj)
135{
136 char *buff = new char[strlen(obj.str) + 1];
137 std::strcpy(buff, obj.str);
138 for (size_t i = 0; i < std::strlen(buff); i++)
139 buff[i] = tolower(buff[i]);
140 Mystring temp(buff);
141 delete[] buff;
142 return temp;
143}
144
145// concatenation
146Mystring operator+(const Mystring &lhs, const Mystring &rhs)
147{
148 char *buff = new char[std::strlen(lhs.str) + std::strlen(rhs.str) + 1];
149 std::strcpy(buff, lhs.str);
150 std::strcat(buff, rhs.str);
151 Mystring temp(buff);
152 delete[] buff;
153 return temp;
154}
155
156// stream insertion operator
157std::ostream& operator<<(std::ostream &os, const Mystring &obj)
158{
159 os << obj.str; // if friend function
160 //os << obj.get_str(); // if not friend function
161 return os;
162}
163
164// stream extraction operator
165std::istream& operator>>(std::istream &is, Mystring &obj)
166{
167 char *buff = new char[1000];
168 is >> buff;
169 obj = Mystring(buff); // since we have efficient move assignemt, it will be called
170 // (steal the pointer)
171 delete[] buff;
172 return is;
173}
Mitropoulos, F. (2022). Beginning C++ Programming - From Beginner to Beyond [Video file]. Retrieved from https://www.udemy.com/course/beginning-c-plus-plus-programming/