Functions
Why functions?
- A compound statement may be needed to execute many times
- You can copy them several times, but…
cofunction.cpp
#include <iostream>
#include <float.h>
struct Matrix
{
int rows;
int cols;
float * pData;
};
int main()
{
using namespace std;
Matrix matA = {3,4};
matA.pData = new float[matA.rows * matA.cols]{1.f, 2.f, 3.f};
Matrix matB = {4,8};
matB.pData = new float[matB.rows * matB.cols]{10.f, 20.f, 30.f};
Matrix matC = {4, 2};
matC.pData = new float[matC.rows * matC.cols]{100.f, 200.f, 300.f};
// some operations on the matrices
float maxa = FLT_MIN;
float maxb = FLT_MIN;
float maxc = FLT_MIN;
//find max value of matA
for(int r = 0; r < matA.rows; r++)
for (int c = 0; c < matA.cols; c++)
{
float val = matA.pData[ r * matA.cols + c];
maxa = ( maxa > val ? maxa : val);
}
//find max value of matB
for(int r = 0; r < matB.rows; r++)
for (int c = 0; c < matB.cols; c++)
{
float val = matB.pData[ r * matB.cols + c];
maxa = ( maxa > val ? maxa : val);
}
//find max value of matC
for(int r = 0; r < matC.rows; r++)
for (int c = 0; c < matC.cols; c++)
{
float val = matC.pData[ r * matC.cols + c];
maxa = ( maxa > val ? maxa : val);
}
cout << "max(matA) = " << maxa << endl;
cout << "max(matB) = " << maxb << endl;
cout << "max(matC) = " << maxc << endl;
delete [] matA.pData;
delete [] matB.pData;
delete [] matC.pData;
return 0;
}
g++ nofunction.cpp --std=c++11
有很多重复代码
- We can put the compound statement into a function
function.cpp
#include <iostream>
#include <float.h>
struct Matrix
{
int rows;
int cols;
float * pData;
};
float matrix_max(struct Matrix mat)
{
float max = FLT_MIN;
//find max value of mat
for(int r = 0; r < mat.rows; r++)
for (int c = 0; c < mat.cols; c++)
{
float val = mat.pData[ r * mat.cols + c];
max = ( max > val ? max : val);
}
return max;
}
Matrix * create_matrix(int rows, int cols)
{
Matrix * p = new Matrix{rows, cols};
p->pData = new float[p->rows * p->cols]{1.f, 2.f, 3.f};
//you should check if the memory is allocated successfully
return p;
}
bool matrix_add(const Matrix & matA, const Matrix & matB, Matrix & matC)
{
// check the dimensions of the three matrices
// re-create matC if needed
// do: matC = matA + matB
// return true if everything is right
return true;
}
int main()
{
using namespace std;
Matrix matA = {3,4};
matA.pData = new float[matA.rows * matA.cols]{1.f, 2.f, 3.f};
Matrix matB = {4,8};
matB.pData = new float[matB.rows * matB.cols]{10.f, 20.f, 30.f};
Matrix matC = {4, 2};
matC.pData = new float[matC.rows * matC.cols]{100.f, 200.f, 300.f};
// some operations on the matrices
float maxa = matrix_max(matA);
float maxb = matrix_max(matB);
float maxc = matrix_max(matC);
cout << "max(matA) = " << maxa << endl;
cout << "max(matB) = " << maxb << endl;
cout << "max(matC) = " << maxc << endl;
delete [] matA.pData;
delete [] matB.pData;
delete [] matC.pData;
return 0;
}
A Question
- If
Matrix::pData
isNULL
or an invalid value, how to tell the calling function from the called one? - The pointer should be checked first!
Where should a function be? Option1
draw.cpp
// The function must be defined before it was called
bool drawLine(int x1, int y1, int x2, int y2)
{
// Source code here
return true;
}
bool drawRectangle(int x1, int y1, int x2, int y2)
{
// some calculation here
drawLine(...);
drawLine(...);
drawLine(...);
drawLine(...);
return true;
}
Where should a function be? Option2
draw.cpp
// declared first, parameter names can be omitted
bool drawLine(int x1, int y1, int x2, int y2);
bool drawRectangle(int x1, int y1, int x2, int y2)
{
// some calculation here
drawLine(...);
drawLine(...);
drawLine(...);
drawLine(...);
return true;
}
// define it later
bool drawLine(int x1, int y1, int x2, int y2)
{
// Source code here
return true;
}
Where should a function be? Option3
draw.h
#ifndef __DRAW_H__
#define __DRAW_H__
bool drawLine(int x1, int y1, int x2, int y2);
bool drawRectangle(int x1, int y1, int x2, int y2);
#endif
draw.cpp
#include <draw.h>
bool drawRectangle(int x1, int y1, int x2, int y2)
{
// some calculation here
drawLine(...);
drawLine(...);
drawLine(...);
drawLine(...);
return true;
}
// define it later
bool drawLine(int x1, int y1, int x2, int y2)
{
// Source code here
return true;
}
main.cpp
#include <draw.h>
int main()
{
//...
drawRectangle(10, 20, );
//...
}
How are functions called?
- A call stack can store information about the active functions of a program
- Store the address the program returns after the function call
- Store the registers
- Store the local variables
- // do some work of the called function
- Restore the registers
- Restore the local variables
- Store the function returned result
- Jump to the return address
- The cost to call a function!
2-Function Parameters
Parameters
- The symbolic name for “data” that passes into a function
Two ways to pass into the function:
- Pass by value
- Pass by reference
Pass by value: fundamental type
- The parameter is a copy of the original variable
param-pointer.cpp
int foo1(int x)
{
x += 10;
return x;
}
int main()
{
int num1 = 20;
int num2 = foo1(num1);
return 0;
}
Will num1
be changed in foo()
?
No
Pass by value: pointer
- What’s the difference?
param-pointer.cpp
int foo2(int * p)
{
(*p) += 10;
return *p;
}
int main()
{
int num1 = 20;
int * p = &num1;
int num3 = foo2( p );
return 0;
}
It still is passing by value (the address) !
A copy of the address
拷贝地址,但是却是不同的指针
param-pointer.cpp
#include <iostream>
using namespace std;
int foo1(int x)
{
cout << "foo1 &x = " << &x << endl;
x += 10;
return x;
}
int foo2(int * p)
{
cout << "foo2 p = " << p << endl;
cout << "foo2 &p = " << &p << endl;
(*p) += 10;
return *p;
}
int main()
{
int num1 = 20;
int num2 = foo1(num1);
cout << "num1=" << num1 << endl;
cout << "num2=" << num2 << endl;
cout << "&num1= " << &num1 << endl;
cout << "&num2= " << &num2 << endl;
int * p = &num1;
cout << "main p = " << p << endl;
cout << "main &p = " << &p << endl;
int num3 = foo2( p );
cout << "num1=" << num1 << endl;
cout << "*p=" << *p << endl;
cout << "num3=" << num3 << endl;
cout << "&num1 = " << &num1 << endl;
cout << "main p = " << p << endl;
cout << "main &p = " << &p << endl;
cout << "&num3 = " << &num3 << endl;
return 0;
}
- 按值传递:基础类型,会将参数的值赋值给函数里的接收变量,在函数里面修改值的内容不会影响函数外部所传递的变量的值。函数里的变量跟外部的变量不是同一个,可以从
&x
和&num1
的不同看出。 - 按值传递:指针,会将指针的地址赋值给函数里的接收变量,在函数里面修改地址指向的内容会直接影响外部指针所指向的内容。但是,函数内部的指针和外部指针不是同一个指针,可以从
foo2 &p
和main &p
的不同看出
Pass by value: structure
- How about structure parameter?
- If the structure is a huge one, such as 1K bytes
- A copy will cost 1KB memory, and time consuming to copy it.
3-References
References in C++
- References are in C++, not in C
- A reference is an alias to an already-existing variable/object
reference.cpp
#include <iostream>
using namespace std;
int main()
{
int num = 0;
int & num_ref = num;
cout << "num = " << num << endl;
num_ref = 10;
cout << "num = " << num << endl;
return 0;
}
- A reference to an object
- A reference must be initialized after its declaration
int & num_ref; // error
Matrix & mat_ref; // error
- Reference VS Pointer: References are much safer
Function parameters with a huge structure
- If the huge struct is passed as a function parameter
struct PersonInfo
{
char firstname[256];
char middlename[256];
char lastname[256];
char address[256];
char nationalID[16];
// and more
};
char * fullname(struct PersonInfo pi)
{
// ...
}
- The data will be copied. Not a good choice!
The problem
- One solution is to use a pointer
struct PersonInfo
{
char firstname[256];
char middlename[256];
char lastname[256];
char address[256];
char nationalID[16];
// and more
};
char * fullname(struct PersonInfo *ppi)
{
if (ppi == NULL)
{
cout << "Invalid pointer" << endl;
return NULL;
}
// ...
}
References as function parameters
- No data copying int the reference version; Better efficiency
- The modification to a reference will affect the original object
param-reference.cpp
#include <iostream>
#include <float.h>
struct Matrix
{
int rows;
int cols;
float * pData;
};
float matrix_max(const struct Matrix & mat)
{
float max = FLT_MIN;
//find max value of mat
for(int r = 0; r < mat.rows; r++)
for (int c = 0; c < mat.cols; c++)
{
float val = mat.pData[ r * mat.cols + c];
max = ( max > val ? max : val);
}
return max;
}
int main()
{
using namespace std;
Matrix matA = {3,4};
matA.pData = new float[matA.rows * matA.cols]{1.f, 2.f, 3.f};
Matrix matB = {4,8};
matB.pData = new float[matB.rows * matB.cols]{10.f, 20.f, 30.f};
Matrix matC = {4, 2};
matC.pData = new float[matC.rows * matC.cols]{100.f, 200.f, 300.f};
// some operations on the matrices
float maxa = matrix_max(matA);
float maxb = matrix_max(matB);
float maxc = matrix_max(matC);
cout << "max(matA) = " << maxa << endl;
cout << "max(matB) = " << maxb << endl;
cout << "max(matC) = " << maxc << endl;
delete [] matA.pData;
delete [] matB.pData;
delete [] matC.pData;
return 0;
}
- To avoid the data is modified by mistakes
4- Return Statement
Return statement
- Statement
return;
is only valid if the function return type isvoid
- Just finish the execution of the function, no value returned
- The return type can be a fundamental type or a compound type
- Pass by value:
- Fundamental types: the value of a constant/variable is copied.
- Pointers: the address is copied
- Structures: the whole structure is copied
Matrix * create_matrix(int rows, int cols)
{
Matrix * p = new Matrix{rows, cols};
p->pData = new float[p->rows * p->cols]{1.f, 2.f, 3.f};
//you should check if the memory is allocated successfully
return p;
}
一进入函数,先做输入输出的检查
每次new之后,检查内存申请是否成功
If we have a lot to return
- Such as a matrix addition function (A+B -> C)
- A suggested prototype:
- To use references to avoid data copying
- To use const parameters to avoid the input data is modified
- To use non-const reference parameters to receive the output
bool matrix_add(const Matrix & matA, const Matrix & matB, Matrix & matC)
{
// check the dimensions of the three matrices
// re-create matC if needed
// do: matC = matA + matB
// return true if everything is right
return true;
}
Similar mechanism in OpenCV
- Matrix add in OpenCV
void cv::add( InputArray src1, InputArray src2, OutputArray dst,
InputArray mask, int dtype )
{
CV_INSTRUMENT_REGION();
arithm_op(src1, src2, dst, mask, dtype, getAddTab(), false, 0, OCL_OP_ADD );
}
5-Inline Functions
Inline Functions
- Stack operations and jumps are needed for a function call
- It is a heavy cost for some frequently called tiny functions
inline.cpp
#include <iostream>
using namespace std;
inline float max_function(float a, float b)
{
if (a > b)
return a;
else
return b;
}
int main()
{
int num1 = 20;
int num2 = 30;
int maxv = max_function(num1, num2);
cout << maxv << endl;
return 0;
}
调用函数代价较高
- The generated instructions by a compiler can be as follows to improve efficiency
直接将代码挪过来,用空间换时间
- inline suggests the compiler to perform that kind of optimizations
- The compiler may not follow your suggestion if the function is too complex o contains some constrains
- Some functions without inline may be optimized as an inline on
Why not use a macros
#define MAX_MACRO(a, b) (a)>(b) ? (a) : (b)
- The source code will be replaced by a preprocessor
- Surely no cost of a function call
- And
a, b
can be any types which can compare
inline.cpp
#include <iostream>
using namespace std;
inline float max_function(float a, float b)
{
if (a > b)
return a;
else
return b;
}
//#define MAX_MACRO(a, b) a>b ? a : b
#define MAX_MACRO(a, b) (a)>(b) ? (a) : (b)
int main()
{
int num1 = 20;
int num2 = 30;
int maxv = max_function(num1, num2);
cout << maxv << endl;
maxv = max_function(num1++, num2++);
cout << maxv << endl;
cout << "num1=" << num1 << endl;
cout << "num2=" << num2 << endl;
maxv = MAX_MACRO(num1, num2);
cout << maxv << endl;
maxv = MAX_MACRO(num1++, num2++);
cout << maxv << endl;
cout << "num1=" << num1 << endl;
cout << "num2=" << num2 << endl;
num1 = 0xAB09;
num2 = 0xEF08;
maxv = MAX_MACRO(num1&0xFF, num2&0xFF);
cout << maxv << endl;
return 0;
}
调用宏和内联函数的区别:
宏会直接把代码文本替换,要注意替换变量出现多次,则不能在使用宏的时候进行操作运算,不然宏会把整个操作运算直接文本替换到宏的内容中,就会造成多次操作。
如果宏不加一个圆括号:
#include <iostream>
using namespace std;
inline float max_function(float a, float b)
{
if (a > b)
return a;
else
return b;
}
#define MAX_MACRO(a, b) a>b ? a : b
//#define MAX_MACRO(a, b) (a)>(b) ? (a) : (b)
int main()
{
int num1 = 20;
int num2 = 30;
int maxv = max_function(num1, num2);
num1 = 0xAB09;
num2 = 0xEF08;
maxv = MAX_MACRO(num1&0xFF, num2&0xFF);
cout << maxv << endl;
return 0;
}
直接文本替换:num1&0xFF>num2&0xFF ? num1&0xFF : num2&0xFF
大于号>优先级更高
实际是:num1&(0xFF>num2)&0xFF ? num1&0xFF : num2&0xFF
⇒
\Rightarrow
⇒ num1&0&0xFF ? num1&0xFF : num2&0xFF
⇒
\Rightarrow
⇒ 0 ? num1&0xFF : num2&0xFF
⇒
\Rightarrow
⇒ 8
宏比较危险
Inline in OpenCV
用得最多的宏CV_ALWAYS_INLINE
: 根据编译器的不同,将inline变成编译器支持的强制性inline