Part 0: Dynamic memory allocation
Part 1: Namespace
Part 2: Using functions
Part 3: Classes
Part 4: Organize codes into modules
Allocating memory at run-time with new
xxxxxxxxxx// Single object allocationRectangle* pRec = new Rectangle; // auto pRec = new Rectangle;// Arrays of objects allocationRectangle* pRecA = new Rectangle[10]; //atuo pRecA = new Rectangle[10] ;
Rectangle * pRecB[10];for(int i=0; i<10; i++) { pRecB[i] = new Rectangle;}Operator delete releases dynamic memory previously allocated with new
xxxxxxxxxx// release single objectdelete pRec;// release arrays of objectsdelete[] pRecA;delete[] pRecB;A common problem in programs are memory leaks
if no delete , then memory is allocated but never released even when it is not used anymore
xxxxxxxxxxvoid leakFunc() { int array2[1000]; int* array = new int[1000];// auto array = new int[1000]; //... do stuff with array /* statement A*/ delete[] array; }int main() { int i; for (i = 0; i < 1000; i++) { leakFunc(); // leak 4K at every call without statement A, } return 0;}
Single global namespace often bad idea
Possibility for conflict: someone else (or even you inadvertently) may have used the name want you use in your new piece of code elsewhere
The namespace feature in C++ allows you to explicitly control the scope of your symbols
xxxxxxxxxxnamespace foo { int global = 0 ; void func() { cout << global << endl; }}code outside namespace must explicitly use scope operator with namespace name to resolve symbol
void bar() { cout << foo::global << endl; foo::func();} Namespaces and the Standard Library
All symbols in the Standard library are wrapped in the namespace ‘std’
<iostream>, <math> etc.
xxxxxxxxxxint main () { std::cout << "Hello World!" << std::endl; return 0;}It is possible to import symbols from a given namespace into the current scope
using std::cout; //Import selected symbols into global namespace using std::endl;int main () { cout << "Hello World!" << endl; return 0;} You can also import the symbol contents of an entire namespace
xxxxxxxxxxusing namespace std ;int main () { cout << "Hello World!" << endl; return 0;}If possible only import symbols you need
The lifetime of automatic variables inside the block is limited to the end of the block
i.e up to the point where the } is encountered.
xusing namespace std;int main() { int i = 1 ; if (1) { int i = 0 ; //Memory for ‘int i’ allocated cout << "inside if statement: i = " << i << endl; } //Memory for ‘int i’ released cout << "outside of if statement: i = " << i << endl; return 0;}
A block introduces a new scope : it is a separate namespace in which you can define new symbols, even if those names already existed in the enclosing block
in C++ functions, arguments can have default values
xxxxxxxxxxdouble line(double x, double slope = 1, double offset = 0);{ return x * slope + offset;}int main() { double x = 1.; double y; y = line(x, 3.7, 5.2);// here slope=3.7, offset=5.2 y = line(x, 3.7); // here offset=0. y = line(x); // uses slope=1, offset=0 return 0;}all arguments without default values must appear to the left of all arguments with default values.
Often algorithms have different implementations with the same functionality, overloaded functions have same name, but different list of arguments.
xxxxxxxxxxint minimum3 (int a, int b, int c) { return (a < b ? ( a < c ? a : c ) : ( b < c ? b : c) ) ;}float minimum3 (float a, float b, float c) { return (a < b ? ( a < c ? a : c ) : ( b < c ? b : c) ) ;}int main() { int a = 3, b = 5, c = 1; float x = 4.5, y = 1.2, z = -3.0; int d = minimum3(a, b, c); // int minimum3() float w = minimum3(x, y, z); // float minimum3() return 0;}Compiler selects appropriate overloaded function based on argument list
pass by values
Function is passed copies of input arguments.
xxxxxxxxxxvoid swap(int a, int b) ;int main() { int a = 3, b = 5; swap(a, b) ; cout << "a=" << a << ", b=" << b << endl ; return 0;}void swap(int a, int b) { //a and b in swap() are copies of a and b in main() int tmp; tmp = a; a = b; b = tmp;} output:
xxxxxxxxxx"a=3, b=5"For objects a copy is passed –If the subroutine changes the object, only the copy is changed
pass by references
xxxxxxxxxxvoid swap(int &a, int &b);int main() { int a = 3, b = 5; swap(a, b); cout << "a=" << a << ", b=" << b << endl; return 0;}// a and b in swap() are references to original a and b in main(). // Any operation affects originals. void swap(int &a, int &b) { int tmp; tmp = a; a = b; b = tmp;} output
xxxxxxxxxx"a=5, b=3"This is the way to return multiple values from a function.
Use const references instead of ‘pass-by-value’ when you are dealing with large objects that will not be changed
pass by pointers
x
void swap(int* a, int* b);int main() { int a = 3, b = 5; swap(&a, &b) ; cout << "a=" << a << ", b=" << b << endl;}// a and b in swap() are pointers to original a and b in main(). // Any operation affects originalsvoid swap(int* a, int* b) { int tmp; tmp = *a; *a = *b; *b = tmp;}Output:
xxxxxxxxxx"a=5, b=3"use references when you can, pointers only when you have to.
Passing array as function parameters
x
void fun(int *arr, unsigned int n){ int i; for (i = 0; i < n; i++) cout << " " << arr[i];}
int main(){ int arr[] = {1, 2, 3, 4, 5, 6, 7, 8}; unsigned int n = sizeof(arr)/sizeof(arr[0]); fun(arr, n); return 0;}
A class representing vector in 2-D.
xxxxxxxxxxclass Vector { public: Vector(); //constructor Vector(double x, double y);//constructor double x() {return m_x;}; //get x double y() {return m_y;}; //get y double r() {return sqrt(m_x * m_x + m_y * m_y);}; //get r double theta(); //get theta void setX(double x) { //set x m_x = x; if(x <0) m_x = -x; } void setY(double y){ //set y m_y = y; if(y <0) m_y = -y; } void setR(double r); //set R void setTheta(double theta);//set Theta private: double m_x; // only non-negative value is allowed double m_y; // only non-negative value is allowed }; //Note the semi-colon after the closing brace.Constructor (构造函数)
The constructor serves to initialize the object
A constructor always has the same name as that of the class.
xxxxxxxxxxpublic: Vector(); Vector(double x, double y);xxxxxxxxxxVector::Vector() { m_x = 0; m_y = 0;}Vector::Vector(double x, double y) { m_x = x; m_y = y; if(x <0) m_x = -x; if(y <0) m_y = -y;}A constructor has no return type.
It is a function that is called when an object is created.
x
Vector u; //calls Vector::Vector()Vector v(1.5, 3.7); // calss Vector::Vector(double x, double y)If we provide no constructors for our class, C++ automatically gives us a default constructor
Member function(成员函数)
xxxxxxxxxxvoid Vector::setR(double R) { double cosTheta = m_x / r(); double sinTheta = m_y / r(); m_x = R * cosTheta; m_y = R * sinTheta;}xxxxxxxxxxVector v(1.5, 3.7);v.setX(2.9); //set v's value of m_x to 2.9double vx = v.x();Pointers to objects
xxxxxxxxxxVector* vPtr; //declare a pointer to Vector class; this doesn't create an object yet!vPtr = new Vector(1.5, 3.7);//create an Vector objectdouble vX = vPtr->x();cout << "vX = "<< vX << endl;// prints vx = 1.5call meber function of an object with ->(not with .)
Information hiding
• Public data
Access is unrestricted. Situation identical to no access control declaration
• Private data
Data objects and member functions in the private section can only be accessed by member functions of the struct (which themselves can be either private or public)
xxxxxxxxxxprivate: double m_x; double m_y;m_x and m_y are declared private, a Vector object’s values of m_x and m_y cannot be accessed directly, but only from within the class’s member functions
xxxxxxxxxxVector v(1.5, 3.7);cout << v.m_x << "," << v.m_y << endl;// Error!Destructor (析构函数)
C++ defines Destructor function for each class to be called at end of lifetime of object
Can be used to release memory, resources before death
xxxxxxxxxxclass File { private: fstream fs; char* filename; public: File(const char* name) { fs = open(name); filename = (char*) name; } ~File() { close(filename); }//destructor //...};Destructor calls can take care of automatic resource control
Example with dynamically allocated File object
xxxxxxxxxxvoid readFromFile() { File *f = new File("theFile.txt"); //Opens file automatically ... // read something from file delete f; ///Deletion of f calls destructor}Example with automatic File object
xxxxxxxxxxvoid readFromFile() { File f("theFile.txt"); //Opens file automatically ... // read something from file} //call destructor of f when f goes out of scope.Inheritance is a technique to build a new class based on an old class
xxxxxxxxxx//Rectangle.hclass Point //基类Point类的声明{ public: //公有函数成员 void InitP(float xx = 0, float yy = 0) {X = xx; Y = yy;} void Move(float xOff, float yOff) {X += xOff; Y += yOff;} float GetX() {return X;} float GetY() {return Y;} private: //私有数据成员 float X, Y;};class Rectangle: public Point //派生类声明部分{ public: //新增公有函数成员 void InitR(float x, float y, float w, float h){ InitP(x, y);//基类构造函数 W = w; H = h; } //调用基类公有成员函数 float GetH() {return H;} float GetW() {return W;} private: //新增私有数据成员 float W, H;};xxxxxxxxxxusing namespace std;void main(){ Rectangle rect; //声明Rectangle类的对象 rect.InitR(2, 3, 20, 10); //设置矩形的数据 rect.Move(3, 2); //移动矩形位置 cout << "The data of rect(X,Y,W,H):" << endl; cout << rect.GetX() << "," //输出矩形的特征参数 << rect.GetY() << "," << rect.GetW() << "," << rect.GetH() << endl; return 0;}Virtual function (虚拟函数)
Often derived classes behave differently:
xxxxxxxxxxclass TDish { . .. virtual bool ForSoup() const = 0; //pure virtual function};class TPlate: public TDish { ... virtual bool ForSoup() { return false; } //implement ForSoup in class TPlate};class TBowl: public TDish { ... virtual bool ForSoup() { return true; } ////implement ForSoup in class TDish};
x
void FillWithSoup(TDish* dish) { if(dish->ForSoup()) dishD->SetFull(); // if a Dish is a Plast then do nothing! }xxxxxxxxxxTDish* a = new TPlate();TDish* b = new TBowl();FillWithSoup(a);// will not be fullFillWithSoup(b);// is now full
usually it is not convenient to keep all C++ source code in a single file.
Split source code into multiple files.
Declarations file
xxxxxxxxxx// capitalize.hh
class Fifo { public: char read();};void convertUpper(char* str);void convertLower(char* str);
- Safeguarding against multiple inclusion in .hh
xxxxxxxxxx//code(#else)Definitions file
xxxxxxxxxx// capitalize.ccchar Fifo::read() { count--; if (front == len) front = 0; return s[front++] ;}void convertUpper(char* ptr) { while(*ptr) { if (*ptr >= 'a' && *ptr <= 'z') *ptr -= 'a'-'A'; ptr++ ; } }void convertLower(char* ptr) { while(*ptr) { if (*ptr >= 'A' && *ptr <= 'Z') *ptr += 'a'-'A'; ptr++ ; } }Main function
xxxxxxxxxx// demo.ccint main(int argc, const char* argv[]) { if (argc != 2) return 0 ; convertUpper(argv[1]) ; std::cout << argv[1] << std::endl ; return 0;}Compile
xxxxxxxxxxg++ demo.cc capitalize.cc -o demoLong compilation scripts are a bit much:
xxxxxxxxxxg++ demo.cc capitalize.cc -o demoSo let's put it in a makefile:
xxxxxxxxxxall: g++ demo.cc capitalize.cc -o demo(note you need a tab at the beginning, not spaces. )
Then run make all