P. 1
MIT6_088IAP10_lec06

|Views: 7|Likes:

See more
See less

08/17/2012

pdf

text

original

# 6.

088 Intro to C/C++

Day 6: Miscellaneous Topics
Eunsuk Kang & Jean Yang

In the last lecture...

Inheritance Polymorphism Abstract base classes

Today’s topics
Polymorphism (again) Namespaces Standard Template Library Copying objects Integer overﬂow

Polymorphism revisited

Polymorphism revisited
Recall: Ability of type A to appear as or be used like another type B. Example:
Rectangle* r = new Square(5.0); // length = 5.0

where Square is a subtype of Rectangle

. then the behavior of a program P must remain unchanged when objects of type T are replaced with objects of type S.Liskov Substitution Principle Photograph removed due to copyright restrictions. .mit.edu/~liskov/.pmg. Please see http://www. Barbara Liskov Winner.Turing Award 08’ If S is a subtype of T.csail.

void setLength(). void setWidth(). void getWidth(). public: Rectangle(float length. } class Square : public Rectangle { // representation invariant: length = width public: Square(float length). void getLength(). // does nothing } . // ensures length = width void setWidth(). void setLength(). float width.Rectangle-Square example class Rectangle { protected: float length. float width).

// does nothing } . void setLength(). Why? class Square : public Rectangle { // representation invariant: length = width public: Square(float length). void setWidth(). void setLength().Rectangle-Square example class Rectangle { protected: float length. // ensures length = width void setWidth(). float width). } Violates the Liskov Substitution Principle. void getWidth(). public: Rectangle(float length. void getLength(). float width.

and “false” in Square. Better: Maybe Square shouldn’t really be a subtype of Rectangle? Change the type hierarchy! Think about behaviors.Solutions Ugly: Modify setWidth and setLength in Rectangle to return a boolean. Make them return “true” in Rectangle. not just characteristics of an object! . Deﬁne a separate method “setDimension” in Square.

They always return “true” in Rectangle. and “false” in Square. Deﬁne a separate method “setDimension” in Square. not just characteristics of an object! . Better: Maybe Square shouldn’t really be a subtype of Rectangle? Re-think the type hierarchy! Think about behaviors.Solutions Ugly: Modify setWidth and setLength in Rectangle to return a boolean.

Solutions Ugly: Modify setWidth and setLength in Rectangle to return a boolean. Better: Maybe Square shouldn’t really be a subtype of Rectangle? Re-think the type hierarchy! Think about behaviors.They always return “true” in Rectangle. Deﬁne a separate method “setDimension” in Square. and “false” in Square. not just characteristics of an object! .

Namespaces .

dodge::SUV s2 = new dodge::SUV().Namespaces �an abstract space that contains a set of names �useful for resolving naming conﬂicts namespace ford { class SUV { . } namespace dodge { class SUV { . } .. }.... }.. } int main() { ford::SUV s1 = new ford::SUV().. .

Using namespaces Use with caution! namespace ford { class SUV { .. ... } int main() { using namespace ford.... class Compact { . }. SUV s1 = new SUV(). }. } // exposes SUV and Compact .

} int main() { //using namespace ford. }...Using namespaces Expose only the things that you need to use! namespace ford { class SUV { . class Compact { .. }. } .... . SUV s1 = new SUV(). using ford::SUV.

..) �iostream (std::cout << .) �and many other things! World!”) The library lives inside the namespace “std” ...C++ standard library & namespace C++ standard library includes: �string (std:string s = “Hello �vector (std::vector<T> .

Using namespace std #include <string> class MITPerson { protected: int id. std::string address. std::string name. . public: MITPerson(int id. std::string address). void displayProfile(). void changeAddress(std::string newAddress). }. std::string name.

}. void displayProfile(). void changeAddress(std::string newAddress). std::string address). public: MITPerson(int id. . std::string name. std::string name. std::string address.Using namespace std #include <string> class MITPerson { protected: int id.

std::string name. }. void displayProfile().Using namespace std #include <string> class MITPerson { protected: int id. std::string address. std::string name. std::string address). void changeAddress(std::string newAddress). I am too lazy to type! . public: MITPerson(int id.

string address). . string name. void displayProfile(). string name. string address. public: MITPerson(int id. class MITPerson { protected: int id. void changeAddress(string newAddress). }.Using namespace std #include <string> using namespace std.

Why? Rules of thumb: �“using std::string” instead of “using namespace std” �include “using. not in the header (why?) Simplest rule: Just type “std::”.. Sacriﬁce a few extra keystrokes in the name of good! .Be aware! This is potentially dangerous.” only in the .cc ﬁle..Using namespace std .

Standard Template Library .

Standard Template Library (STL) �a set of commonly used data structures & algorithms �parameterized with types Some useful ones include: �vector �map �stack. queue.com/wiki/stl/start .cppreference. priority_queue �sort More available at: http://www.

// creates a vector with 100 ints std::vector<T> v3(100). // creates a vector with 100 elements primitives initialized to some default value objects created using default constructor .Example using vectors �an array with automatic resizing std::vector<T> v. // creates an empty vector of type T elements std::vector<int> v2(100).

2 = sophomore. etc.h" class Student : public MITPerson { int course. int course.h" "Class. int year. }. std::vector<Class*> classesTaken. // 1 = freshman. void addClassTaken(Class* newClass). std::string address. int year). . void displayProfile(). public: Student(int id.Student class from the last lecture #include #include #include #include <iostream> <vector> "MITPerson. std::string name. void changeCourse(int newCourse).

void addClassTaken(Class* newClass). }. etc. int year). public: Student(int id. std::vector<Class*> classesTaken.h" don’t forget! class Student : public MITPerson { int course. declares an empty vector of pointers to Class objects .h" "Class. void changeCourse(int newCourse). 2 = sophomore.A list of classes as a vector #include #include #include #include <iostream> <vector> "MITPerson. std::string address. void displayProfile(). int course. // 1 = freshman. int year. std::string name.

getName() << “\n”.01”).Vector operations Class* c1 = new Class(“6. Class* c2 = new Class(“6.push_back(c2).pop_back(). // prints “6. classesTaken. .01” // prints “6. // accessing an element Class* c3 = classesTaken[0].pop_back().push_back(c1). // inserting a new element at the back of the vector classesTaken. classesTaken.empty()) std::cout << “Vector is empty!\n”.at(1). Class* c4 = classesTaken.getName() << “\n”. std::cout << c3.005”).005” // removing elements from the back of the vector classesTaken. // checking whether the vector is empty if (classesTaken. std::cout << c4.

push_back(c1).01” // prints “6.pop_back(). std::cout << c4. // accessing an element Class* c3 = classesTaken[0]. Class* c2 = new Class(“6.getName() << “\n”.005” // removing elements from the back of the vector classesTaken.getName() << “\n”.empty()) std::cout << “Vector is empty!\n”. // inserting a new element at the back of the vector classesTaken. classesTaken. // prints “6.Vector operations Class* c1 = new Class(“6. Class* c4 = classesTaken.pop_back().push_back(c2). std::cout << c3. .01”).005”).at(1). // checking whether the vector is empty if (classesTaken. classesTaken.

Class* c4 = classesTaken. // accessing an element Class* c3 = classesTaken[0]. classesTaken. // prints “6.push_back(c2). // checking whether the vector is empty if (classesTaken.getName() << “\n”. std::cout << c3.01”).01” // prints “6.getName() << “\n”.at(1). .pop_back(). classesTaken.Vector operations Class* c1 = new Class(“6.005” // removing elements from the back of the vector classesTaken. std::cout << c4. Class* c2 = new Class(“6.empty()) std::cout << “Vector is empty!\n”. // inserting a new element at the back of the vector classesTaken.push_back(c1).pop_back().005”).

005”).pop_back().getName() << “\n”.005” // removing elements from the back of the vector classesTaken.pop_back().push_back(c2).01” // prints “6. std::cout << c3. // inserting a new element at the back of the vector classesTaken.getName() << “\n”. classesTaken. // accessing an element Class* c3 = classesTaken[0]. classesTaken. Class* c4 = classesTaken. // checking whether the vector is empty if (classesTaken.empty()) std::cout << “Vector is empty!\n”.at(1). . std::cout << c4. Class* c2 = new Class(“6. // prints “6.push_back(c1).01”).Vector operations Class* c1 = new Class(“6.

005 . // step through every element in the vector for (it = classesTaken. std::cout << c->getName() << "\n".end(). std::cout << "Classes taken:\n". it++){ Class* c = *it.Traversing a vector using an iterator // display a list of classes taken by the student // create an iterator std::vector<Class*>::iterator it.01 6.begin(). } Classes taken: 6. it != classesTaken.

Traversing a vector using an iterator // display a list of classes taken by the student // create an iterator std::vector<Class*>::iterator it. it != classesTaken. } type for the iterator increment iterator iterator to the beginning of the vector iterator to the end of the vector .end(). // step through every element in the vector for (it = classesTaken. std::cout << "Classes taken:\n".begin(). it++){ Class* c = *it. std::cout << c->getName() << "\n".

} it a pointer to an element *it the element . std::cout << c->getName() << "\n".begin(). std::cout << "Classes taken:\n".end().Traversing a vector using an iterator // display a list of classes taken by the student // create an iterator std::vector<Class*>::iterator it. // step through every element in the vector for (it = classesTaken. it++){ Class* c = *it. it != classesTaken.

http://www. Used with permission.cppreference.com/wiki/stl/vector/start .There are many other functions Courtesy of C++ Reference.

Copying objects .

print(p2).”). “32 Vassar St.displayProfile().Objects as values So far we’ve dealt mostly with pointers to objects. MITPerson p2 = p1. } 36 . void print(MITPerson p){ p. } int main() { MITPerson p1(921172. “James Lee”. What if you want to pass around objects by value? For example.

} int main() { MITPerson p1(921172. so make a copy void print(MITPerson p){ p. initialization by value.displayProfile().”). “James Lee”. MITPerson p2 = p1. so make a copy (3.Creating an object from another 1. } 2. pass by value. could also return an object as a return value) 37 . print(p2). “32 Vassar St.

} So how do objects get copied? Copy constructors are called. “32 Vassar St.Copying objects using constructors void print(MITPerson p){ p.displayProfile(). “James Lee”.. } 38 . print(p2). MITPerson p2 = p1. } int main() { MITPerson p1(921172.”). Object::Object(const Object& other) { ..

std::string address). std::string name. void changeAddress(std::string newAddress). public: MITPerson(int id. std::string name. void displayProfile(). std::string address. }. MITPerson(const MITPerson& other).Copy constructor in MITPerson #include <string> class MITPerson { protected: int id. 39 .

id.Deﬁning the copy constructor why const? object that you are copying from MITPerson::MITPerson(const MITPerson& other){ name = other. id = other.name. address = other. } 40 .address.

Default copy constructor �automatically generated by the compiler �copies all non-static members (primitives & objects) �invokes the copy constructor of member objects MITPerson::MITPerson(const MITPerson& other){ name = other. address = other. id = other. } 41 .name.id.address.

“Alice Smith”. does NOT create a new object So how do objects get assigned to each other? Copy assignment operator is called. MITPerson p2(978123. “121 Ames St.. Object& Object::operator=(const Object& other) { . p2 = p1.”). “32 Vassar St.. “James Lee”. } 42 .”). // assigns p2 to p1.Assigning an object to another MITPerson p1(921172.

void changeAddress(std::string newAddress).Copy assignment operator in MITPerson #include <string> class MITPerson { protected: int id. std::string address). std::string name. 43 . std::string name. std::string address. public: MITPerson(int id. }. MITPerson(const MITPerson& other). void displayProfile(). MITPerson& operator=(const MITPerson& other).

“Alice Smith”.id.”).address. “121 Ames St.name.”). “32 Vassar St.Deﬁning the copy assignment operator MITPerson& MITPerson::operator=(const MITPerson& other){ name = other. p2 = p1. return *this. “James Lee”. // returns a newly assigned MITPerson 44 . id = other. address = other. } MITPerson p1(921172. MITPerson p2(978123.

address = other. if you don’t deﬁne one.name.id.address. } Again.Deﬁning the copy assignment operator MITPerson& MITPerson::operator=(const MITPerson& other){ name = other. the compiler will automatically generate a copy assignment operator So why do we ever need to deﬁne copy constructors & copy assignment operators ourselves? 45 . id = other. return *this.

foo(a1).printB(). } ~A() { delete pb. void foo(A a) { a. } 46 . return 0.printB().Default copy constructor .caution! class B { public: void print() { std::cout << "Hello World!\n”. int x. } }. } }. class A { B* pb. a1. } int main() { A a1(5). public: A(int y) : x (y) { pb = new B(). } // destructor void printB() { pb->print().

} }. int x. } int main() { A a1(5). } }.printB(). void foo(A a) { a.printB(). } Double free! How do we ﬁx this? 47 .caution! class B { public: void print() { std::cout << "Hello World!\n”. } // destructor void printB() { pb->print(). public: A(int y) : x (y) { pb = new B(). return 0. foo(a1). a1.Default copy constructor . class A { B* pb. } ~A() { delete pb.

x. int x. // clean up the junk in the existing object! pb = new B(). } ~A() { delete pb. return *this. delete pb. pb = new B(). } void printB() { pb->print(). class A { B* pb. } A(const A& other) { // copy constructor x = other.caution! class B { public: void print() { std::cout << "Hello World!\n”.x. 48 . } }.Default copy constructor . } }. } // destructor A& operator=(const A& other) { // copy assignment operator x = other. public: A(int y) : x (y) { pb = new B().

then you should deﬁne all three (you will probably need them!) �destructor �copy constructor �copy assignment operator 49 .Rule of three in C++ If you deﬁne any one of the three in a class.

Integer overﬂow .

Used with permission. based on from Joshua Bloch’s implementation in java.1. int high = length .Binary search algorithm int binarySearch(int a[]. int key.util 51 . // key found } return -(low + 1). if (midVal < key) low = mid + 1 else if (midVal > key) high = mid . while (low <= high) { int mid = (low + high) / 2.1. else return mid. } // key not found Courtesy of Joshua Bloch. int midVal = a[mid]. int length) { int low = 0.

1. 52 . Used with permission. int length) { int low = 0.Binary search bug int binarySearch(int a[]. while (low <= high) { int mid = (low + high) / 2. int high = length . int midVal = a[mid]. else return mid.1. int key. // key not found } Can you ﬁnd the bug? Courtesy of Joshua Bloch. // key found } return -(low + 1). if (midVal < key) low = mid + 1 else if (midVal > key) high = mid .

int midVal = a[mid].1.1. while (low <= high) { int mid = (low + high) / 2. if (midVal < key) low = mid + 1 else if (midVal > key) high = mid . int key. it will overﬂow! dangerous array access! (Java will at least throw an exception) Courtesy of Joshua Bloch. else return mid. int length) { int low = 0. Used with permission. int high = length . } if (low + high) > MAX_INTEGER.Binary search bug int binarySearch(int a[]. } return -(low + 1). // key found // key not found 53 .

use: int mid = low + (high .blogspot.util) http://googleresearch. Surprisingly.low) / 2. most implementations of the binary search tree had this bug! (including java.One solution Instead of: int mid = (low + high) / 2.html 54 .com/2006/06/extra­ extra-read-all-about-it-nearly.

Conclusion .