/* When an object is created and initialised at the same time (in a declaration like item x = y; or when an object is passed to a function as a normal parameter), the COPY CONSTRUCTOR for that type of object is used. Only the copy constructor is called, and no official assignment is involved. The copy constructor for a class called item must be declared like this: item(const item & orig) and usually looks like this: item(const item & orig) { member1 = orig.member1; ... member2 = orig.member2; } but of course, you can redefine it to give it special behaviour if needed. When an assignment to a normal object variable occurs, such as item x; x = y; the assignment method for that class is called. Assignment methods are defined like this: item & operator=(const item & orig) { member1 = orig.member1; ... member2 = orig.member2; return *this; } I didn't get around to mentioning the last detail in class today. It is expected that an assignment operator method will always return a reference to the object that was assigned to. It doesn't have to do that, but if it doesn't, people will say you are not writing "proper" C++. Other operators may be defined outside of a class, but the assignment operator must ALWAYS be a method of its class. That is the little rule that I forgot today, which was the cause of a surprise compilation error. For example, I could define a + operator between items and ints either with a method of the item class: item operator+(int n) { item result; result.member1 = member1 + n; result.member2 = member2 - n; return result; } or with an independent non-class function like this item operator+(item x, int n) { item result; result.member1 = x.member1 + n; result.member2 = x.member2 - n; return result; } Whichever is defined would be called if ever the program does something like this: item x, y; ... y = x + 1; In the first case, it would be converted to y.operator=(x.operator+(1)); and in the second it would be converted to y.operator=(operator+(x, 1)); but you can't have both. Note also that the += operator has nothing to do with either the + operator or the = operator. It is totally independent. For example, this little program struct item { int a, b; item(int x) { a = x; b = x; } item(const item & x) { a = x.a; b = x.b; } item & operator=(const item & x) { a = x.a; b = x.b; return * this; } item operator+(int y) { item r = *this; r.a = r.a + y; r.b = r.b - y; return r; } item & operator+=(int x) { a = a * x; b = 100 + x; return * this; } void print() { cout << "a is " << a << ", b is " << b << "\n"; } }; void main() { item one(5); item two(5); one.print(); two.print(); one = one + 3; two += 3; one.print(); two.print(); } would print a is 5, b is 5 a is 5, b is 5 a is 8, b is 2 a is 15, b is 103 */ #include #include #include struct note { string text; int date; // text and date are MEMBERS note(string s) // this is a CONSTRUCTOR { text=s; date=time(NULL); cout << "note constructor '" << text << "' " << ctime(&date) << "\n"; } note(const note & a) { cout << "note copy constructor '" << a.text << "'\n"; text=a.text; date=a.date; } note & operator=(const note & src) { cout << "note '" << text << "' overwritten with '" << src.text << "'\n"; text = src.text; date = src.date; cout << " text and date copied\n"; } virtual void print() // print is a METHOD all methods are also MEMBERS { cout << "note::print '" << text << "' " << ctime(&date) << "\n"; } }; // note is the BASE class or SUPERCLASS // prionote is the SUBCLASS or DERIVED class and INHERITS from note struct prionote: public note { int priority; prionote(string s, int p): note(s) // says how to call note constr. { priority=p; cout << "prionote constructor '" << text << "' p=" << priority << "\n"; } prionote(const prionote & a): note(a.text) { cout << "prionote copy constructor '" << a.text << "'\n"; date=a.date; priority=a.priority; } prionote(const note & a): note(a.text) { cout << "prionote copy constructor '" << a.text << "'\n"; date=a.date; priority=0; } prionote & operator=(const prionote & src) { cout << "prionote '" << text << "' overwritten with '" << src.text << "'\n"; text = src.text; date = src.date; priority = src.priority; cout << " text, date, and priority copied\n"; } virtual void print() { cout << "prionote::print '" << text << "' p=" << priority << "\n"; } }; // a=b is really the same as a.operator=(b) // a prionote is a kind of note // anything doable to a note is also doable to a prionote struct nonymousnote: public note { string sender; nonymousnote(string s, string from): note(s) // says how to call note constr. { sender=from; cout << "nonymousnote constructor '" << text << "' from " << sender << "\n"; } virtual void print() { cout << "nonymousnote::print '" << text << "' from " << sender << "\n"; } }; void explain(note a) { cout << "explaining note "; a.print(); } void explain(prionote a) { cout << "explaining prionote"; a.print(); } /* The first illustrative main void main() { note a("I'm note a"); prionote b("I'm note b", 2); explain(a); explain(b); a.print(); // uses note::print because a declared as note b.print(); // uses prionote::print same reason a=b; note * p = new prionote("I'm the third note", 3); explain(*p); note * q = new nonymousnote("I'm the third note", "Fritz"); p->print(); // uses note::print because p decl as ptr to note // this is static binding of methods q->print(); } */ // whenever a member or method is accessed using a . like this // item.member = 7; // item.method("hello", 9); // the choice about which version of member or method to use // is entirely made by how item was declared. Whatever item's // type is, that version of the method is called. "virtual" // will not make any difference. // whenever a non-method member is accessed in any way // item.member = 7; // pointer->member = 8; // it is still entirely a type-based decision. "virtual" applies // only to methods. // whenever a method is NOT declared as virtual, all accesses // item.method("hello", 9); // pointer->method("warm", 10); // it is still entirely a type-based decision. // all of those are examples of STATIC DISPATCH // ONLY when a METHOD is accessed through a POINTER and was declared // as VIRTUAL in the base type, will DYNAMIC DISPATCH be used. // Then, at the moment the method is called in the running program, // the method from the appropriate subclass is chosen. // a.print() note's print is used always // p->print() AND note's print is declared virtual // then correct print version chosen when program running // a.text note's text is used // p->text note's text is used // This is the main used to illustrate assignment methods and copy // constructors. void main() { note a("I'm note a"); prionote b("I'm note b", 2); prionote c=a; a=c; a.print(); b.print(); c.print(); }