520 likes | 624 Views
C++ Part II. Yingcai Xiao. Header files and Makefile Command line arguments: argc and argv Introduction to pointers: C++1.ppt Advanced pointer concepts Arrays Pointer arithmetic Functional pointers Virtual functions CORBA. TOC. C++ Preprocessing, Compilation and Linking.
E N D
C++Part II Yingcai Xiao
Header files and Makefile • Command line arguments: argc and argv • Introduction to pointers: C++1.ppt • Advanced pointer concepts • Arrays • Pointer arithmetic • Functional pointers • Virtual functions • CORBA TOC
C++ Preprocessing, Compilation and Linking
Source File (.cpp) Preprocessing Compilation Linking Intermediate File (.I) Object File (.obj) Binary File (.exe) • C++ “Compilation”
copy everything in the include files into the .cpp file • replace all symbolic constants • remove all white spaces: \n \t and space. • preprocessing flags • #include : copy everything in the include files • e.g. #include <iostreams> // system include files • #inlcude “Rectangle.h” // user defined files • #define: string substitution • e.g. #define PI 3.1415926 • #ifdef • #endif • e.g #ifdef DEBUG • avoid multiple inclusion of header files • #ifndef • #define • #endif Preprocessing
Header (include) files • extension: .h • contain function headers for non-OOP coding (e.g. math.h) • contain class definitions for OOP coding (e.g. iostreams.h) • needed for the compiler to perform type checking • distributed as text file with software libraries • implementations are hided in .cpp files • Implementation files • extension: .cpp • contain function implementation for non-OOP coding • contain class implementation for OOP coding • compiled into .obj files • obj files are binary files Separating Class Definition and Class Implementation
Header file: Rectangle.h class Rectangle{ protected: int x, y; public: Rectangle (); Rectangle (int cx, int cy); double Area( ); }; Methods have only headers, no bodies Separating Class Definition and Class Implementation
Implementation file: Rectangle.cpp #include "Rectangle.h" using namespace std; Rectangle::Rectangle () { x=0; y=0; } Rectangle::Rectangle (int cx, int cy) { x=cx; y=cy; } double Rectangle::Area( ) { return x*y; } Separating Class Definition and Class Implementation
Implementation file: Rectangle.cpp #include <iostream> // system header files #include “Rectangle.h” // user defined header files using namespace std; int main() { Rectangle rect1; cout << rect1.Area() << "\n"; cout << "Please enter 'q' to quit.\n "; char a; cin >> a; } Separating Class Definition and Class Implementation
#ifndef RECTANGLEH #define RECTANGLEH class Rectangle{ protected: int x, y; public: Rectangle (); // only header, no body Rectangle (int cx, int cy); double Area( ); }; #endif Preprocessing
cygwin • pwd • /cygdrive/c/Documents and Settings\xiao • is mapped to • C:\Documents and Settings\xiao • mkdir oop • copy main.cpp Rectangle.cpp and rectangle.h to the directory • g++ main.cpp Rectangle.cpp –I. –o sep.exe • ./sep • Compilation flags: • -I : search path for include files • -o : output exe file name • -l : add libraries files • -c : generating object files • -g : generating debugging info • -D : defining symbolic constants • -DDEBUG => #define DEBUG 1 Compilation
create a makefile with dependencies (C++Examples\MyRectangle) • sep : main.o Rectangle.o MyRectangle.o • g++ main.o Rectangle.o MyRectangle.o -o sep.exe • main.o : main.cpp • g++ -c main.cpp • Rectangle.o : Rectangle.cpp • g++ -c Rectangle.cpp • MyRectangle.o : MyRectangle.cpp • g++ -c MyRectangle.cpp • in cygwin, type • make • make –f makefile • make will compare time stamps of the files and only recompile files being modified since last compilation. • make will check the dependencies from top to bottom. Makefile
Command Line Arguments • LINUX
When running a program from the command line, you can pass arguments to the main function of the program. e.g.: mvcnc M1 V1 2 • Each argument is passed as a null terminated string of characters to the main. e.g. “M1” • Argument strings are grouped together by an array of pointers to strings of characters: char * argv [] • “argc” counts the total number of arguments passed, including the command name (e.g. mvcnc). • If you need an integer, use “atoi” to convert a string to an integer. • argc and argv are not object oriented. In Java and C#, they are merged into one as an array of String objects. Command Line Arguments
int main(int argc, char* argv[]) { cout << "Your have typed: "; for(int i = 0; i < argc; i++) cout << argv[i] << " "; cout << "\n"; if(argc != 4) { cerr << "Usage: " << argv[0] << " Model View n(# of integer to process)." << endl; exit(1); } int n = atoi(argv[3]); cout << "You requested to process " << n << " integers. \n"; return(1); } Example
Cygwin: a UNIX emulator for Windows. • All programs->Cygwin->Cygwin Bash Shell • cp T:/Xiao/OOP/C++/mvcnc.exe . • (Use “/” instead of “\” as separators in UNIX.) • ./mvcnc • ./mvcnc M1 V2 3 Command Line Arguments in Cygwin
All programs->MS Visual Studio-> MS Visual Studio 2005 • File->Open->Project/Solution • Select the existing project • In the Solution Explorer pane, click on the project name • Right click to bring up the Property menu. • Configuration Properties->Debugging->Command Arguments • Type in the desired arguments. • e.g. M1 V2 3 • The arguments will be passed in when Debug->Run Command Line Arguments in V.S.
What? • UNIX, operating system for workstations, by Ken Thompson, Dennis Ritchie and Douglas McIlroy. http://en.wikipedia.org/wiki/Unix. • LINUX, UNIX for PCs, Linus Torvalds, http://www.linux.org/ • Fedoro, LINUX-based PC OS, http://fedoraproject.org/ • Start LINUX in the labs: • Reboot PC in the lab, hit “return” before Windows starts. • GRUB, GRand Unified Bootloader, loads BIOs (Basic IO). • Select Fedoro 2.6.21-1. • User Name / Password Command Line Arguments in LINUX
Use LINUX: • Applications->System Tools->Terminal • mkdir oop • cd oop • rm file-name • rmdir directory-name • Places->CD/DVD Creator • Create, Compile and Run Programs • Applications->Accessories->Text Editor • Type you code and save it to mvcnc.cpp • c++ mvcnc.cpp –o mvcnc.exe (g++ mvcnc.cpp –o mvcnc.exe) • ./mvcnc M1 V2 3 Command Line Arguments in LINUX
ClassName ObjectName; “Rectangle rect;” declares a reference of class Rectangle. rect “rect” is the name of a memory space that stores a reference. Instantiating a Class in Java A “reference” is an internal pointer, it needs to “point” to an object before being used.
Class Rectangle { protected: int x, y; Public: Rectangle () {x=0;y=0;} Public: Rectangle (int cx, int cy) {x=cx; y=cy;} Public: double area( ) { return x*y; } } x y References in Java
Rectangle rect; double area = rect.Area(); // will not compile in Java Rectangle rect = new Rectangle (); // Use the first constructor rect x 0x12345678 y References in Java and C# • Dereferencing is automatic for a reference in Java. (No *rect) • double area = rect.Area(); • Please note the notation difference between a “pointer” and a “name”.
int i; In both Java and C++: “i” is the name of a memory space that stores an integer value. int i = 8; i is a value type, for which we can directly store an integer value into the memory named as i. Compiler already allocated memory to store the value and we don’t need to “new” to allocate memory to store the value. i Value Types in Java and C++
Class Name; In C++: “Rectangle rect;” declares an object of class Rectangle. “rect” is a value type. rect Instantiating a Class in C++ “rect” is the name of a memory space that stores a Rectangle object.
Rectangle *rect; double area = rect->Area(); // Wrong! Compiles. Try! Rectangle *rect = new Rectangle (); // Correct, use the first constructor rect 0x12345678 Pointers in C++ double a = (*rect).Area(); double b = rect->Area(); • The type of a pointer tells the compiler the layout of the memory pointed to by the pointer. • (Please note the notation difference between a “pointer” and a “name”.)
Variables of value types have the object (not the pointer/reference) memories allocated by the compiler and can be used as an object directly without “new”. Variables of pointer/reference types have the pointer/reference (not the object) memories allocated by the compiler and can not be used as an object directly without “new”. In Java and C#: Rectangle rect; // rect is a reference rect = new Rectangle (3, 4); // rect reference an object of allocated memory location double a = rect.Area(); // use “.” to dereference In C++: Rectangle *rect; // rect is a pointer rect = new Rectangle (3, 4); // rect points to an object of allocated memory location double a = (*rect).Area(); // use “* and .” to dereference double b = rect->Area(); // use “->” to dereference In C++: Rectangle rect; // rect is a value type object of allocated memory location double a = rect.Area(); // use “.” to dereference Value Types v.s. Pointer/Reference Types
Value Types are Stack Objects: memories allocated at compile time on the stack automatically freed when the objects are out of scope less overhead, code runs faster less flexible, sizes need to be known at compile time • Pointer /Reference Types point to Heap Objects: • memory are dynamically allocated at run time on the heap • stays there even if the pointers/references are out of scope • dynamically allocated memories need to be freed / deleted manually • more flexible, sizes need not to be known at compile time • more overhead, code runs slower Value Types v.s. Pointer/Reference Types
In Java, dynamically allocated memories are automatically freed by the garbage collector. In C++, there is no garbage collector. dynamically allocated memories needs to be freed manually using free (C++ syntax) or delete (C syntax). Rectangle *rect; // rect is a pointer rect = new Rectangle (1, 2); // rect points to an object of allocated memory location double a = rect->Area(); // use “->” to dereference free(rect); // delete rect; rect = new Rectangle (3, 4); // rect points to another object of allocated memory location double a = rect->Area(); // use “->” to dereference free(rect); // delete rect; Free Dynamically Allocated Memory
Never dereference pointers before allocating memories to them. • Never dereference pointers before assigning values to the objects they point to. Dynamically allocated memories are not clean in C++. • Don’t forget to free the dynamically allocated memories after using them. This will cause memory leak. No one else will be able to use the memories allocated by your program before you free them. • Never free the same memory more than once. • Never free stack memory (used by value variables); • C++ compilers allow you to make all of the above mistakes. Causing big problems for others that you even don’t know. • To avoid the problems, assign 0 to pointers when they are created and deleted. Only use the pointers when they are not equal to 0, i.e., if(pointer) is true. • Rectangle *rect = 0; // rect is a pointer • rect = new Rectangle (1, 2); // rect points to an object of allocated memory location • if(rect) double a = rect->Area(); // use “->” to dereference • if(rect) free(rect); rect = 0; // free the memory and reset the pointer to 0; The Five+1 “Never”s
Write down the output of the following code and draw a picture to show the memory structure. class Point { public: int x; int y; }; Point p1; p1.x = 1; p1.y = 2; Point p2 = p1; // Copies p1 cout << p1.x << “ “ << p1.y; cout << p2.x << “ “ << p2.y; p2.x = 3; p2.y = 4; cout << p1.x << “ “ << p1.y; cout << p2.x << “ “ << p2.y; Point *p3; p3 = & p1; cout << p1.x << “ “ << p1.y; cout << p3->x << “ “ << p3->y; p3->x = 5; p3->y = 6; cout << p1.x << “ “ << p1.y; cout << p3->x << “ “ << p3->y; Code Example
Find and describe the errors in the following code. class Point { public: int x; int y; }; Point p1; p1.x = 1; p1.y = 2; Point *p3; cout << p3->x << “ “ << p3->y; p3 = & p1; cout << p3->x << “ “ << p3->y; delete p3; p3 = new Point(); cout << p3->x << “ “ << p3->y; delete p3; free (p3); Code Example
Find and describe the errors in the following code. p3 = new Point(); p3->x = 3; p3->y = 4; Point *p4 = p3; cout << p4->x << “ “ << p4->y; delete p3; delete p4; Point *p5; int i = 1; if(i == 1) { Point p6; p5 = & p6; Point *p7 = new Point(); } cout << p5->x << “ “ << p5->y; Code Example
Arrays in C++ int a[2]; a[0] = 5; a[1] = 10; cout << a[0] << “ “ << a[1]; // stack objects, size has to be a constant and can’t be changed at runtime. int size = 2; int b[size]; // will not compile in C++ // An array name represents a constant pointer, can’t change its value. a int size; cin >> size; // assuming user entered 2. int *p; // a pointer p = new int[size]; p[0] = 5; p[1] = 10; cout << p[0] << “ “ << p[1]; // heap objects; dynamically allocated at runtime, “size” can be a variable delete p; // free the memory. p int *p2; // a pointer p2 = a; p2[0] = 50; p2[1] = 100; cout << a[0] << “ “ << a[1]; delete p2; // Don’t do this.
Pointer Arithmetic Pointers can be moved back and forth by one object with ++ and -- p int a[2] = {5,10}; int *p; // a pointer p = a; cout << p[0]; p++; cout << p[0]; p--; cout << p[0]; p++; cout << p[1]; Watch out where you are with a pointer!
TIC++V1:C15 Type Cast Virtual Function Polymorphism
class Rectangle{ protected: int x, y; public: Rectangle () {x=0;y=0;} Rectangle (int cx, int cy) {x=cx; y=cy;} double Area( ) { return x*y; } }; Rectangle rect; x y Type Cast
class Rectangle{ protected: int x, y; public: Rectangle () {x=0;y=0;} Rectangle (int cx, int cy) {x=cx; y=cy;} double Area( ) { return x*y; } }; x y class MyRectangle: public Rectangle{ public: void Set(int dx, int dy){x=dx; y=dy;} }; MyRectangle rect2; Type Cast void Set(int dx, int dy)
Rectangle rect1; Rectangle *p1 = &rect1; cout << p1->Area(); // ok p1->Set(1,1); // won’t compile MyRectangle *p2; // implicit cast to a child class, will not compile p2 = p1; //explicit cast to a child class, allowed p2 = (MyRectangle *) p1; cout << p2->Area(); // ok p2->Set(1,1); //will compile, cause runtime error Implicit type cast a pointer to its child class (down cast) is not permitted. Explicit type cast a pointer to its child class (down cast) is permitted, but should be used with care. rect1 x p1 p2 y Down Cast : casting a pointer to a child class
MyRectangle rect2; • MyRectangle *p2 = &rect2; • cout << p2->Area(); // ok • p2->Set(1,1); // ok • Rectangle *p1; • p1 = p2; // implicit cast to parent, ok • //explicit cast to parent, ok • p1 = (Rectangle *) p2; • cout << p1->Area(); // ok • p1->Set(1,1); // will not compile • MyRectangle *p3; • //explicit cast to child class, ok • p3 = (MyRectangle *) p1; • cout << p3->Area(); // ok • p3->Set(1,1); // ok • Implicit or explicit type cast a pointer to its parent class (up cast) is permitted. • Up cast is important for using a predefined event loop. Shape->Draw(); • Down cast should only be used when the original object is an instance of the child class. rect2 x p2 p1 y Up Cast : casting a pointer to the parent class void Set(int dx, int dy)
void Reset(MyRectangle rectf) { // rectf is the formal parameter, it has its own memory. rectf.Set(1,1); cout << rectf.Area(); // 1 } void main () { MyRectangle rect; cout << rect.Area(); // 0 Reset(rect); // rect is the actual parameter cout << rect.Area(); // 0 } Pass-by-value: The value of the actual parameter is copied to the formal parameter when a method is called. The value of the formal parameter is not copied to the actual parameter when the method returns. The formal parameter expires when the method returns. The value of the actual object can not be changed by the called method. Passing by Value
void Resetp(MyRectangle *rectp) { // rectp is the formal parameter, a pointer. rectp->Set(1,1); cout << rectp->Area(); } void main () { MyRectangle rect; cout << rect.Area(); // 0 Resetp(&rect); // &rect is the actual parameter cout << rect.Area(); // 1 } Pass-by-pointer: The address of the object is copied to the pointer formal parameter when a method is called. When the pointer is dereferenced the actual object (rect in the main) is accessed. The value of the actual object can be changed by the called method. x rectp y rect Passing by Pointer void Set(int dx, int dy)
A reference is an alias of an object, it references the same memory of the object. void main () { MyRectangle rect; cout << rect.Area(); // 0 // declare and initialize a reference MyRectangle &rectr = rect; cout << rectr.Area(); // use the reference as the object } rectr x y rect Reference void Set(int dx, int dy)
Void Reset(MyRectangle &rectr) { // rectr is the formal parameter, a reference. rectr.Set(1,1); cout << rectr.Area(); // 1 } void main () { MyRectangle rect; cout << rect.Area(); // 0 // rect is the actual parameter, its reference is passed. Reset(rect); cout << rect.Area(); // 1 } Pass-by-reference: The formal parameter of a method is a reference of the actual object (rect in the main), when accessing the formal parameter you are really accessing the actual object. Its value can be changes by the called method. rectf x y rect Passing by Reference void Set(int dx, int dy)
A function name is a pointer. • Taking the address of a function gives its address. • Therefore: function-name == &function-name • For challenges and fun, try the examples at • http://www.cs.uakron.edu/~xiao/oop/fun-ptrs.html • A function returning an int A function returning an int pointer A function prototype • A function pointer to a function returning an int A function pointer to a function returning an int pointer • An array of function pointers to functions returning ints An array of function pointers to functions returning int pointers A pointer to a function pointer to a function returning an int An array of pointers to function pointers to functions returning ints A pointer to a function pointer to a function returning an int pointer An array of pointers to function pointers to functions returning int pointers Function Pointers
Connecting a function call to a function body is called binding • When binding is performed before the program is run (by the compiler and linker), it’s called early binding, static binding or compile-time binding. • When binding occurs at runtime, based on the type of the object, it is called late binding, dynamic binding or runtime binding. • The key words, virtual, causes late binding in C++. • If a function is declared as virtual in the base class, it is virtual in all the derived classes Binding
The keyword virtual tells the compiler it should not perform early binding • The compiler creates a single table (called the VTABLE) for each class that contains virtual functions. • The compiler places the addresses of the virtual functions for that particular class in the VTABLE. • In each class with virtual functions, it secretly places a pointer, called the vpointer (abbreviated as VPTR), which points to the VTABLE for that object. • When you make a virtual function call through a base-class pointer (that is, when you make a polymorphic call), the compiler quietly inserts code to fetch the VPTR and look up the function address in the VTABLE, thus calling the correct function and causing late binding to take place. Late Binding in C++
Four objects are newed at runtime: a Wind, a Percussion, a Stringed and a Brass. • Their pointers are all upcasted into Instrument *. • When executing the “what” functions, the correct “what” belonging to the classes, not the one in “Instrument”, are used. • This is called polymorphism, since one type of function pointer, “Instructment->what”, actually points to different functions and therefore behaves differently at different situations. • Polymorphism says which function to call is not determined by the type of the pointer but by the type of the objects its is pointed to. Polymorphism is achieved through late binding of those functions in the VTABLE. • In C++, the “virtual” keyword causes late binding. • Note “Instrucment *ip” behaves polymorphically too even though all the objects it points to are “auto” objects. • Now remove “virtual” in front of “what” in Instrucment. What happends? • Also note, there is no “adjust” function in Brass, so it is bound to the “adjust” in Wind. Instrument.cpp: an Example of Late Binding and Polymorphism in C++