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 allocation
Rectangle* pRec = new Rectangle; // auto pRec = new Rectangle;
// Arrays of objects allocation
Rectangle* 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 object
delete pRec;
// release arrays of objects
delete[] 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
xxxxxxxxxx
void 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
xxxxxxxxxx
namespace 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.
xxxxxxxxxx
int 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
xxxxxxxxxx
using 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.
x
using 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
xxxxxxxxxx
double 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.
xxxxxxxxxx
int 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.
xxxxxxxxxx
void 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
xxxxxxxxxx
void 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 originals
void 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.
xxxxxxxxxx
class 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.
xxxxxxxxxx
public:
Vector();
Vector(double x, double y);
xxxxxxxxxx
Vector::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(成员函数)
xxxxxxxxxx
void Vector::setR(double R)
{
double cosTheta = m_x / r();
double sinTheta = m_y / r();
m_x = R * cosTheta;
m_y = R * sinTheta;
}
xxxxxxxxxx
Vector v(1.5, 3.7);
v.setX(2.9); //set v's value of m_x to 2.9
double vx = v.x();
Pointers to objects
xxxxxxxxxx
Vector* vPtr; //declare a pointer to Vector class; this doesn't create an object yet!
vPtr = new Vector(1.5, 3.7);//create an Vector object
double vX = vPtr->x();
cout << "vX = "<< vX << endl;// prints vx = 1.5
call 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)
xxxxxxxxxx
private:
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
xxxxxxxxxx
Vector 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
xxxxxxxxxx
class 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
xxxxxxxxxx
void 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
xxxxxxxxxx
void 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.h
class 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;
};
xxxxxxxxxx
using 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:
xxxxxxxxxx
class 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!
}
xxxxxxxxxx
TDish* a = new TPlate();
TDish* b = new TBowl();
FillWithSoup(a);// will not be full
FillWithSoup(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.cc
char 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.cc
int main(int argc, const char* argv[])
{
if (argc != 2) return 0 ;
convertUpper(argv[1]) ;
std::cout << argv[1] << std::endl ;
return 0;
}
Compile
xxxxxxxxxx
g++ demo.cc capitalize.cc -o demo
Long compilation scripts are a bit much:
xxxxxxxxxx
g++ demo.cc capitalize.cc -o demo
So let's put it in a makefile:
xxxxxxxxxx
all:
g++ demo.cc capitalize.cc -o demo
(note you need a tab
at the beginning, not spaces. )
Then run make all