660 likes | 680 Vues
CS3101-2 Programming Languages – C++ Lecture 3. Matthew P. Johnson Columbia University Fall 2003. Agenda. hw1 was due last night Today: Pointer review Classes Operator overloading hw2 TBA tonight. Ptrs, references, and things. int x = 4; //thing &x //address of thing
E N D
CS3101-2Programming Languages – C++Lecture 3 Matthew P. Johnson Columbia University Fall 2003 CS3101-2, Lecture 2
Agenda • hw1 was due last night • Today: • Pointer review • Classes • Operator overloading • hw2 TBA tonight CS3101-2, Lecture 2
Ptrs, references, and things • int x = 4; //thing • &x //address of thing • int *xp = &x; //ptr to thing • *xp = 10; //thing • int &rx = x; //reference to thing • rx = 11; //thing • x = 12; //same thing: x == rx • int *rxp = ℞ //ptr to thing • *rxp = 13; /*same thing: *rxp == rx == x */ CS3101-2, Lecture 2
Pointer & reference e.g. • int x = 5; int y = 10; int& xr = x; int *yp = &y; rx *= 3; • x == 15, xr == 15, y == 10, *yp == 10 • *yp = rx; rx++; • x == 16, xr == 16, y == 15, *yp == 15 • yp = ℞ rx++; • x == 17, xr == 17, y == 15, *yp == 17 CS3101-2, Lecture 2
Composite data • We have primitive vars • individual pieces of data • okay as far as they go • We have arrays: • fixed, but arbitrarily large seq of vars • but only one element type • We also have: structs • complex entities with named members • record with fields: vars, arrays, structs, etc. • access with dot: mystruct.field1 CS3101-2, Lecture 2
struct e.g.: stack • Stack: FILO data structure • ~ stack of trays in lunch room • const int STACK_SIZE = 100; struct Stack { int count; int data[STACK_SIZE]; }; • Struct declarations end with ;! CS3101-2, Lecture 2
stack struct: init, push void stack_init(Stack &stack) { stack.count = 0; } inline void stack_push(Stack &stack, const int item) { stack.data[stack.count++] = item; } NB: • we pass in a reference to our stack • we don’t need “struct Stack” as in C • we set the count-th mem to item; then inc count CS3101-2, Lecture 2
stack struct: pop inline int stack_pop(Stack &stack) { return stack.data[--stack.count]; } NB: • we decrement the count and then return the mem • inline expands ftn-call to ftn content • as though we searched for calls to stack_pop and replaced with its two lines of code • don’t need to add ftn call to C’s call stack • used for fast, often called ftns • replaces CPP macros CS3101-2, Lecture 2
struct stack e.g. • Stack stack; • push_stack(5); • data[count++] = 5 data[0] == 5, count == 1; • push_stack(10); • data[count++] = 10 data[1] == 10, count == 2; • x = pop_stack(); • return data[--count] count == 1, ret data[1] == 10 • Right? CS3101-2, Lecture 2
struct stack init • Well, no. • In push_stack(5); • we assumed size == 0 but never set • we write to data[random index] • So we initialize first • Now: Stack stack; init_stack(stack); push_stack(5); … • Q: What if we forget to call init_stack? • A: Don’t! CS3101-2, Lecture 2
More trouble… • Stack s; init_stack(s); int x = pop_stack(s); //empty! • push(s, 2); push(s, 3); init_stack(s); //lost 2 and 3! • s.data[0] = 5; //legal! • s.count = -10; //legal! • Eventually, we run out of room in data CS3101-2, Lecture 2
Classes • Structures are better than nothing, but: • no way to force init at beginning • no way to group all struct’s ftns together • anyone can modify struct’s members • stack.count = -10; • stack.push(5); write to data[-10] • must keep passing struct ref to ftns • must put “stack” in ftn names • Better idea: place all the struct’s ftns inside the struct struct + member ftns == class CS3101-2, Lecture 2
class Stack: declaration/stack.h class Stack { private: // visible only inside the class Stack int count; int data[STACK_SIZE]; public: // visible from anywhere void init(); void push(const int item); int pop(); }; NB: class def must end with a ; • Access modifiers are for sections, not individual mems (as in J) • Sections can come in any order, be repeated, etc. • Default access level is private • Both ftns and data can be both public and private CS3101-2, Lecture 2
class Stack implementation/stack.cpp void Stack::init() { count = 0; } inline void Stack::push(const int item) { data[count++] = item; } inline int Stack::pop() { return data[--count]; } • Like before, but Stack:: before each ftn name CS3101-2, Lecture 2
class Stack e.g. • Stack stack; stack.init(); stack.push(5); stack.push(10); int x = stack.pop(); NB: • Access (public) data and ftns with dot op • Member ftn uses curr inst’s data • Public member ftns provide interface to inst’s data CS3101-2, Lecture 2
class Stack constructor • Class is big improvement over struct • But still must manually call init() after each instantiation • Turns out: can write a constructor(s) • ftn with same name as class • called automatically when class inst created • automatically creates member vars (count, data) • no return type (not void!) • Stack::Stack() { count = 0; } CS3101-2, Lecture 2
class Stack destructor • Conversely, classes can have a (unique) destructor • ftn with name ‘~’ + class name, no return type • called automatically when class inst destroyed • automatically frees class member vars • Not needed for this Stack, but could give error msg: • Stack::~Stack() { if (count != 0) cerr << “Destroying non-empty” << “ stack.\n”; } CS3101-2, Lecture 2
class Stack destructor • Notice destructor name: ~Stack • Bitwise op ~ is the complement op: • applied to int, each bit is flipped • ~1001 == 0110 • 1001 + 0110 == 1111 • constructor + destructor == 0 (well, -1 in 2’s comp) • Intuition: destructor undoes the work of the constructor CS3101-2, Lecture 2
Parameterized constructors • So far, no-param constructor: Stack stack; • Constructors can also take params • Consider an (x,y) point class: • class Point { private: double x, y; public: double getX(), getY(); void setX(), setY(); Point(double a, double b); } Point::Point(double a, double b) { x = a; y = b; } CS3101-2, Lecture 2
Parameterized constructors • Point pt(10,10); • Looks diff from J: Point pt = new Point(10,10); • Reason: C++ classes do not imply dynamic memory! • But if: Point pt; we get an error • If no constructors defined, no-param constructor is automatically defined (simply creating member vars) • Otherwise, must declare/define it ourselves: • class Point { private … public: Point() {}; } • NB: we can define ftns inside the class declaration • usually just for short ftns; automatically inline CS3101-2, Lecture 2
Copy constructors • One special parameterize constructor: one constructor, of class type • Consider: Stack stack1 = stack2; • Q: What happens? • A: The value of a struct/class is composed of/ specified by the vals of all members • Each mem of stack1 init-ed to corr. mem of stack2 • stack1.count set to stack2.count; members of stack1.data set to corr. mems of stack2.data CS3101-2, Lecture 2
Copy constructor • Default copy constructor does do this • Think of it as: Stack::Stack(const Stack &that) { for (int i = 0; i < that.count; i++) data[i] = that.data[i]; count = that.count; } • Q: Why can we access private count mem of that? • A: We’re accessing it from the Stack class • Q: What if data were pointing to dynamic memory? • A: Just a minute… CS3101-2, Lecture 2
Copy constructor • Copy constructor is called implicitly: • Stack stack2 = stack1; expands to Stack stack2(stack1); • ()/= equiv applies to primitives as well: • int x(10); • Used, even more implicitly in pass-by-val: • void myftn(Stack local) { … } myftn(stack1); • Used, even more implicitly in temporary return value: • Stack myftn2() { … return someStack; } stack3 = myftn2(); • Lesson 1: copy constructor specifies what we mean by value • Lesson 2: uses references for class params and return values whenever possible CS3101-2, Lecture 2
Assignment operator • Closely related to the copy constructor is the assignment operator • Stack s2 = s1; • copy constructor used to initialize s2 with s1 • Stack s3; s3 = s2; • assignment op used to reset s3 with s2 • Q: Difference? • A: copy const. create member objects and inits; in assign op, member objects already exist • current content must be destroyed so objs can be reset • Assign op also called implicitly: • stack3 = myftn2(); • Show tdstack.cpp CS3101-2, Lecture 2
Copy const, assign op, destructor • By default, copy constructor and assignment op do natural thing: copy each member var • “shallow copy” • Usually need one iff need all three • Usually need all three iff using dynamic memory CS3101-2, Lecture 2
Default member ftns • Classes have built-in, default no-param constructor, destructor, copy constructor, assignment op • Suppose want to disallow use of one • Soln 1: Print message when called: • class Stack { public: Stack(const Stack &that) { cerr << “not allowed\n”; } } } • But we only find out at runtime CS3101-2, Lecture 2
Prevent constructor call • Soln 2: Make constructor private: • class Stack { private: Stack(Stack &that) {} … } • Now call to copy constructor (outside of Stack) will not compile CS3101-2, Lecture 2
structs v. classes • Turns out: structs in C++ can contain member ftns as well • Further turns out: only difference between classes and C++ structs is: • default class access level: private • default class access level: public • otherwise, struct and class can technically be used interchangeably CS3101-2, Lecture 2
Where do classes live? • Generally, 1 class ~ 2 files: • .h: header/interface/declaration • .cpp: implementation • Header contains • class Stack { void ftn2(); int ftn2(); } • .cpp contains • void Stack::ftn1() { … } • int Stack::ftn2() { … } CS3101-2, Lecture 2
Where do classes live? • .cpp files for classes used are compiled along with other .cpp files: • g++ myproj.cpp stack.cpp • .h files for classes used are included with CPP: • include <iostream> • include “stack.h” • For uses-written headers (at least), include line is replaced with file content • multiple includes can cause trouble CS3101-2, Lecture 2
Multiple includes • Suppose our program includes both stack.h and stackutil.h • But stackutil.h includes stack.h • #include “stack.h” #include “stackutil.h” • • #include “stack.h” #include “stack.h” … • • class Stack { … } class Stack { …} … error CS3101-2, Lecture 2
Multiple includes • We prevent this problem with a (rare) use of CPP • The content of every .h file is surrounded by: • #ifndef _FILENAME_H #define _FILENAME_H //actual content #endif • The first time filename.h is included in a file, we define _FILENAME_H and include the content • Any future time, _FILENAME_H is recognized, so we jump to the #endif • there’s no more code, so the include has no effect CS3101-2, Lecture 2
Conts & pts review • Remember: type-expr *p means that when p is dereferenced, we get something of type type-expr • So Q: what are: • const int *cip = &x; int const *cip = &x; • A: ptr-to-const • Q: What is: • int *const IP = &x; • Q: const ptr • Q: What are: • const int *const CIP; int const *const CIP; • A: const ptr-to-const CS3101-2, Lecture 2
const vars • Constant primitive vars are immutable • like const primitives in C, final primitives in Java • const double PI = 3.14; PI = 4; error/warning • Constant objects more complicated • const string s = “hi”; s = “there”; error • s.at(0)++; error s.append(“abc”); error • Value of object values of all members • Cannot change indy mems or assign (change all mems) • NB: Java’s reference/content distinction doesn’t arise here • in J: objects vars always ref vars • final String s = …; cannot reassign s; mutability of contents determined by class • C++ has non-ref objects const refers to object content CS3101-2, Lecture 2
const objects • Only constant member ftns can be called on const objects: • class MyString { private: … public: int size() const; // not const int size(); void append(const char *str); int &at(int pos) const; } • On const object: • can call size • cannot call append • can call at, but its result will refer to a const CS3101-2, Lecture 2
const data members • Classes can contain const data members • Init-ed with special syntax in constructor • class MyMath { public: const double PI = 3.14; // does not compile! const double PI; const double E; MyMath() : PI(3.14), E(2.71) {} … } • We don’t have this restriction for … static members CS3101-2, Lecture 2
Static members • Suppose want to keep track of total current number of instances of some class in existence • each time we construct, inc total • each time we destruct, dec total • Class con/destructor have access to this total • but not specific to any instance • member of class, not of an instance thereof • Big idea: static data is part of the cookie cutter • non-static data is part of a particular cookie CS3101-2, Lecture 2
Static data members • class Stack { public: static insts = 0; //init-ed once Stack() { insts++; } ~Stack() { insts--; } } • Stack s; // Stack::insts == 1 if (1 == 1) { Stack stacks[10]; // Stack::insts == 11 } // Stack::insts == 1 • Access static members with ClassName::MEM • Can also use standard instance.MEM • but considered impolite CS3101-2, Lecture 2
statics • Also: straightforward init of static consts is allowed • since unchanging, most static members can be class members • class MyMath { private: static const PI = 3.14; … } • Member ftns can also be static: • class Stack { private: static insts = 0; public: static int getInstCount(); … } • Can access insts through getter: • Stack::getInstanceCount() • Static ftns can only access static data members CS3101-2, Lecture 2
Meanings of “static” • File-level var/ftn: not visible outside of file • second construal: var init-ed only once • external makes visible outside file (although this is the default) • Local var (in a ftn): var init-ed only once • not for each ftn call • Var in a class: var init-ed only once • not for each class instance • Ftn in a class: can access only static data members CS3101-2, Lecture 2
Operator overloading • Overloading: multiple behaviors for the same syntax • Operator overloading: defining behavior of an operator when applied to a class • E.g, +/- applies to built-in numerical types • When we define our Point class, representing an (x,y) point • can overide +/- to behave appropriately • (2,3) + (5,5) == (7,8) (picture vectors) CS3101-2, Lecture 2
Point class • class Point { private: double x, y; public: double getX(), getY(); Point() : x(0), y(0) {} Point(double xx, double yy) : x(xx), y(yy) {} Point(const Point &that) : x(that.x), y(that.y) {} Point operator+(const Point& that); Point operator-(const Point& that); Point operator-(); … } CS3101-2, Lecture 2
Adding points • Point p1(3,4), p2(10,10); • Point p3 = p1 + p2; • error: + not defined • Point Point::operator+(const Point& that) { Point temp; temp.x = this->x + that.x; temp.y = this->y + that.y; return temp; } • p1 + p2 expands to p1.operator+(p2) CS3101-2, Lecture 2
Adding points’ • Same behavior, more succinct • Point Point::operator+(const Point& that) { return Point(this->x + that.x, this->y + that.y); } • Text uses this bad/no-OOP style: • Point Point::operator+(const Point& that, const Point& that2) { return Point(this.x + that2.x, that.y + that2.y); } • Poor style – not allowed in homework! CS3101-2, Lecture 2
Operator overloading • Now p1 + p2 works; Suppose p1 + 5? • Should this be allowed? Does it make sense? • Maybe we want: (x,y) + 5 == (x+5,y+5) • Then can overload thus: • Point Point::operator+(const int v) { return Point(this->x + v, this->y + v); } CS3101-2, Lecture 2
Operator overloading • Now p1 + 5 works; Q: does 5 + p1 work? • A: No: would expand to 5.operator+(p1), which doesn’t exist • Instead, must write global ftn with two params • Point operator+(int v, Point& that) { return Point(v + that.x, v + that.y); } • Problem: x, y are inaccessible here • Soln 1: use a friend function CS3101-2, Lecture 2
Friend functions • A class can declare another class or ftn its friend • the friend can access private members of the class • NB: A declaring B its friend B has access to A, not other way around • friendship is not transitive! • class Point { … friend Point operator+(int v, Point& that); } CS3101-2, Lecture 2
x, y inaccessible • Soln 2: Provide getters to x, y • class Point { public: double getX(), getY(); … } • but we don’t always want to make data publicly accessible • Soln 3: use existing + op: • Point operator+(int v, Point& that) { return that + v; } CS3101-2, Lecture 2
Operator overloading • Can override all standard math ops: • + - * / • etc. • Can override [] • Interp here? maybe [0] x, [1] y • double Point::operator[](int pos) { return pos == 0 ? x : y; } • C++ smart enough to convert p[i] to p.operator[](i) CS3101-2, Lecture 2
Overloading << • Suppose want to print a point • Non-OO: manually extract, then print each double • OO: pass to stream; design to behave appropriately • ostream& operator<<(ostream& out, const Point& pt) { out << “(“ << pt.x << “,” << pt.y << “)”; return out; } • Here, op<< must be a friend or use getters • op<< must return the stream for cascaded outputs: • cout << “my point: ” << pt; op<<(cout, “my point :”) << pt op<<(op<<(cout, “my point :”), pt) op<<(cout, pt) cout CS3101-2, Lecture 2