【CPP】函数

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 is NULL 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
  1. Store the address the program returns after the function call
  2. Store the registers
  3. Store the local variables
  4. // do some work of the called function
  5. Restore the registers
  6. Restore the local variables
  7. Store the function returned result
  8. 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:

  1. Pass by value
  2. 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 &pmain &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 is void
  • 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:
  1. Fundamental types: the value of a constant/variable is copied.
  2. Pointers: the address is copied
  3. 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:
  1. To use references to avoid data copying
  2. To use const parameters to avoid the input data is modified
  3. 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

参考:OpenCV Matrixadd

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

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值