C++primer plus第7-8章函数笔记

来源:互联网 发布:网络招标注册流程 编辑:程序博客网 时间:2024/05/16 12:42

第七章

  • 数组作为函数的参数:
    常规变量传入是传入变量的拷贝,而传入数组是传入数组的地址。
int sum_arr(int arr[], int n){    int total = 0;    for (int i = 0; i < n; i++)        total = total + arr[i];    return total; }

方括号表示可传入任意长度,arr是指针而不是数组,但在函数中看作数组来处理。或者写为int sum_arr(int * arr, int n)是等价的。以下两个式子是等价的:
arr[i] == * (arr + i);
&arr[i] == ar + i

  • 若为二维数组,函数声明为int sum(int *ar2[4], int size);或者int sum(int ar2[][4], int size);
    ar2[r][c] == * ( * (ar2+r) + c)是等价的。

  • 一种数组提前结束循环的方式:

int fill_array(double ar[], int limit){    using namespace std;    double temp;    int i;    for (i = 0; i < limit; i++)    {        cout << "Enter value #" << (i + 1) << ": ";        cin >> temp;        if (!cin)    // bad input        {            cin.clear();            while (cin.get() != '\n')                continue;           cout << "Bad input; input process terminated.\n";           break;        }        else if (temp < 0)     // signal to terminate            break;        ar[i] = temp;    }    return i;}

若不想让函数改变数组可以这样声明:void show_array(const double ar[], int n);

  • 使用数组区间的函数:
int sum_arr(const int * begin, const int * end){    const int * pt;    int total = 0;    for (pt = begin; pt != end; pt++)        total = total + *pt;    return total; }

sum = sum_arr(cookies, cookies + 3);
sum = sum_arr(cookies + 4, cookies + 8);
调用中开头是要调用的数组对象,结束是需要调用的最后一个数组对象的下一个位置。

  • 指针和const注意以下几点

    • const int *pt指向的对象的值不能变,即不能利用pt指针去改变它所指对象的值。
    • int * const pt表示指针的指向不能变,即该指针是一个常量。
    • const变量必须由const指针去指向,const指针不一定指向const变量,若指向常规变量,可以通过常规变量改变值,不能通过指针改变值。
  • char数组字符串和string字符串作为函数参数

unsigned int c_in_str(const char * str, char ch){    unsigned int count = 0;    while (*str)        // quit when *str is '\0'    {        if (*str == ch)            count++;        str++;        // move pointer to next char    }    return count; }
void display(const string sa[], int n){    for (int i = 0; i < n; i++)        cout << i + 1 << ": " << sa[i] << endl;}
  • 函数指针:

    • 函数名即为函数地址:
      process(think);
      thought(think());
      前者是传递函数地址,后者是传递函数返回值。

    • 因为函数名即为函数地址:所以double pam(int)是函数,double ( * pf)(int)也是函数。pf是一个指向pam的指针。注意( * pf)的括号不能省略,否则表示返回值是一个指针。

    • 在调用过程中,可以用函数地址调用,也可以用指针调用:即pf和(*pf)是等价的。例如:
      pf = pam;
      pam(4);
      pf(5);
      (*pf)(5);

    • const double * f1(const double ar[], int n);
      const double * f2(const double ar[], int n);
      const double * f3(const double * ar, int n);
      以上三种形式表示同一个函数。
      若声明指向他们的指针其实就把f1用( * p1 )代替即可。即为const double * ( * p1)(const double *, int ) = f1;
      利用C++11的自动类型推断,可写为auto p2 = f2;
      若声明一个他们的指针数组,[]运算符优先级高于*,所以形式如下:
      const double * ( * p1[3])(const double *, int );
      若要声明指向以上数组的指针,则表示[3]之前的类型就应该是一个指针,形式如下:
      const double * ( * ( * pd)[3])(const double *, int );

第八章

  • 内联函数的编译代码与其他程序代码”内联”起来了.编译器将使用相应的函数代码替换函数调用.内联函数的运行速度比常规函数稍快,但代价是需要占用更多内存.所以应该有选择的使用内联函数.

    • 要是用这项特性,必须采取下述措施之一:
      在函数声明前加上关键字inline
      在函数定义前加上关键字inline
  • 引用更接近const指针,必须在创建时进行初始化,一旦与某个变量关联起来,就将一直效忠于它.如下:rats和rodents的值和地址都相同。

int rats;int & reodents = rats;

和指针相比:rodents和*prats和rats等价;&rodents和prats和&rats等价。相当于rodents只是rats的另外一个名字而已。

  • 引用作为函数的参数是引用传递,即把变量的地址给了函数,函数可以直接改变变量的值。
    比较引用传递和指针传递:
void swapr(int & a, int & b)    // use references{    int temp;    temp = a;       // use a, b for values of variables    a = b;    b = temp;}void swapp(int * p, int * q)    // use pointers{    int temp;    temp = *p;      // use *p, *q for values of variables    *p = *q;    *q = temp;}

调用时:swapr(wallet1, wallet2);
swapp(&wallet1, &wallet2);

  • 在实参和引用形参不匹配时,且参数为const引用时,C++会生成临时变量。例如:
double refcube(const double &ra){    return ra*ra*ra;}
  • 返回类型是引用的函数:
sysop & use(sysop & sysopref){    cout << sysopref.name << " says:\n";    cout << sysopref.quote << endl;    sysopref.used++;    return sysopref; }

如果不希望返回的引用被更改,可以在最前面加上const。如:const sysop & use(sysop & sysopref)
注意:不能用函数结束会释放的内存作为引用返回值。

  • 带默认参数的函数:
    只在原型指定默认值,函数定义不变。
char * left(const char * str, int n = 1);int main(){}char * left(const char * str, int n){    if(n < 0)        n = 0;    char * p = new char[n+1];    int i;    for (i = 0; i < n && str[i]; i++)        p[i] = str[i];  // copy characters    while (i <= n)        p[i++] = '\0';  // set rest of string to '\0'    return p; }
  • 函数重载:
    定义名称相同的函数,通过函数特征标来区别调用哪个函数。
    类型引用和类型本身视为同一个特征标。
    返回类型可以不同,特征标也必须不同。不能只通过返回类型来重载。

  • 函数模板:

template <typename T>  // or class Tvoid Swap(T &a, T &b){    T temp;   // temp a variable of type T    temp = a;    a = b;    b = temp; }

比如在结构体中,不想用模板同时操作所有结构体数据,则可以显示具体化,编译器会优先考虑具体化定义,不会先调用模板,例如:

struct job{    char name[40];    double salary;    int floor;};template <typename T>void Swap(T &a, T &b)    {    T temp;    temp = a;    a = b;    b = temp;}// 只交换salary和floortemplate <> void Swap<job>(job &j1, job &j2)  // 具体化{    double t1;    int t2;    t1 = j1.salary;    j1.salary = j2.salary;    j2.salary = t1;    t2 = j1.floor;    j1.floor = j2.floor;    j2.floor = t2;}

如果想定义一个int类型的模板,则可以用显示实例化:
template void swap < int >(int, int);
注意:
隐式实例化:编译器根据所给参数的类型生成相应类型的模板函数实例
显式实例化:编译器指定的类型生成相应类型的模板函数实例
模板特化(显式具体化):对特定类型的需要专门编写的模板函数实例

template< typename T >
void swap(T a,T b){}

template void swap < int >(int a,int b);//显式实例化

template<> void swap < int > (int a,int b);//模板特化,针对int类型专门编写的函数

template<> void swap < float >(float a,float b);//模板特化,针对float类型专门编写的函数

  • 编译器匹配问题:
    遵循规则:完全匹配,提示转换,标准转换,用户定义的转换。
    在同时完全匹配后会选择最佳匹配。
    一般而言,指向非const指针和引用优先,非模板函数优先,较具体的模板优先。
0 0
原创粉丝点击