Lecture 16 - Oct 12, 2023
Summary
In this lecture, we conclude our discussion on operator overloading. We introduce copy constructors and how shallow the default copy constructors are.
Last lecture
Operator overloading: operator+
, operator<<
.
Today
Copy constructor.
Recap shift operator
(3,4);
Complex z<< z; cout
For the operator<<
, the rhs is Complex
and the lhS I ostream
(not Complex
).
cout
is an object of ostream
class.
operator<<
cannot be a member function of Complex
, since the lhs is of type ostream
.
operator<<
cannot be a non-mmember function as it has to access private members of Complex
.
Solution. operator<<
can be a friend function.
A friend function can access private members of an object, and lhs can be a non-complex type.
class Complex
...
public:
...
friend ostream& operator<<(ostream&os, const Complex& x);
;}
// not ostream& Complex::operator<<
& operator<<(ostream&os, const Complex& x) {
ostream<< x.real << " + " << x.img << "i";
os return os;
}
Remember that operator<<
is not a member, so there is no need for Complex::
.
int main() {
(3,4);
Complex z<< "z =" << z << endl;
cout }
Recall the diagram from lecture 15.
cout
is returned to allow chained cout.
cout
(and all other streams) cannot be parsed or returned by value. Only by reference, as their copy constructor is deleted.
Copy constructor
Copy constructor is a constructor used to create a copy of an existing object. When is the copy constructor called?
Student a(b);
,b
is an object of student.Student a = b;
, create and initialize on the same line. Common confusion
;
Student a= b; // here a.operator=(b) is invoked a
- Passing an object by value to a function.
- Return an object by value from a function.
By default, every class has a copy constructor that copies all data members.
class Student {
private:
;
string nameint ID;
public:
// copy constructor
(const Student& other) {
Student= other.name;
name = other.ID;
ID }
};
We have to pass by reference, or else there will be a compile-time error. If passed by value, the copy constructor will be called again, leading to an infinite recursion.
Constructors has no returns.
Problem. What happens when data members are pointers?
class MyString {
private:
int len;
char* buf;
public:
() { len = 0; buf = nullptr; }
MyString
(char* src) {
Mystring= new chat[srtlen(src) + 1];
buf (buf, src);
strcpy= strlen(src);
len }
void setString(char* src) {
if (!buf = NULL) {
delete[] buf;
= new char[strlen(src) + 1];
buf (buf, src);
strcpy= strlen(src);
len }
}
// shallow copy!
(const MyString&x) {
MyString= x.len;
len = x.buf;
buf }
};
int main() {
("Hello");
MyString a(a);
MyString b.setString("Oops"); // we change the string in a and b
breturn 0;
}
Solution. Deep copy
::MyString(const MyString& src) {
MyString= new char[strlen(src) + 1];
buf (buf, src.buf);
strcpy= src.len;
len }
int main() {
("Hello");
MyString a(a);
MyString b.setString("Oops"); // we change the string in a and b
breturn 0;
}
Rule of three
If a class requires one of the following, then it almost certainly needs all three.
- User-defined destructor.
- User-defined copy constructor.
- User-defined assignment operator
operator=
.