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

第七章
  • 数组作为函数的参数:
    常规变量传入是传入变量的拷贝,而传入数组是传入数组的地址。
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 T
void 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和floor

template <> 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值