《C++ primer plus》学习笔记——第八章函数探幽

一、C++内联函数

(1)C++区别于C语言,提供了新特性:内联函数,引用传递变量,默认的参数值,函数重载(多态)以及模板函数;
(2)C++内联函数的优势:
在这里插入图片描述
在这里插入图片描述

(3)写内联函数的要求:
(a)在函数声明前加上关键字inline;
(b)在函数定义前加上关键字inline;
(c)内联函数不能递归

(4)什么时候用内联函数呢?
代码量不多,且经常使用
(5)eg如下:
在这里插入图片描述

#include<stdio.h>
#include<stdlib.h>


// inline.cpp -- using an inline function
#include <iostream>
// an inline function definition
inline double square(double x) { return x * x; }
int main()
{
    using namespace std;
    double a, b;
    double c = 13.0;

    a = square(5.0);
    b = square(4.5 + 7.5);   // can pass expressions
    cout << "a = " << a << ", b = " << b << "\n";
    cout << "c = " << c;
    cout << ", c squared = " << square(c++) << "\n";
    cout << "Now c = " << c << "\n";

	system("pause");
	return 0;
}

在这里插入图片描述

说明:
(1)
在这里插入图片描述
(2)内联函数与宏定义的区别
在这里插入图片描述

二、引用变量:C++新增

(1)引用变量的意思:
在这里插入图片描述
(2)创建引用变量:
(a)C和C++使用&符号来指示变量的地址。C++给&符号赋予了另一个含义,将其用来声明引用。
(b)eg1:在这里插入图片描述
eg2:

#include<stdio.h>
#include<stdlib.h>


// firstref.cpp -- defining and using a reference
#include <iostream>
int main()
{
    using namespace std;
    int rats = 101;
    int & rodents = rats;   // rodents is a reference

    cout << "rats = " << rats;
    cout << ", rodents = " << rodents << endl;
    rodents++;
    cout << "rats = " << rats;
    cout << ", rodents = " << rodents << endl;

// some implementations require type casting the following
// addresses to type unsigned
    cout << "rats address = " << &rats;
    cout << ", rodents address = " << &rodents << endl;

	system("pause");
	return 0;
}

在这里插入图片描述
说明:
(a) int & rodents = rats; // rodents is a reference
意思是:将rodents的类型声明为int &,即指向int变量的引用
(b) cout << ", rodents address = " << &rodents << endl;
意思是:&运算符是地址运算符,其中&rodents表示rodents引用的变量的地址
(c)引用和指针的区别如下:
在这里插入图片描述
相似点:
在这里插入图片描述在这里插入图片描述

不同点:引用还是不同于指针的!
第一点:
必须在声明引用时将其初始化,而不能像指针那样,先申明,再赋值。
在这里插入图片描述
第二点:
在这里插入图片描述

(3)eg:
在这里插入图片描述

#include<stdio.h>
#include<stdlib.h>


// secref.cpp -- defining and using a reference
#include <iostream>
int main()
{
    using namespace std;
    int rats = 101;
    int & rodents = rats;   // rodents is a reference

    cout << "rats = " << rats;
    cout << ", rodents = " << rodents << endl;

    cout << "rats address = " << &rats;
    cout << ", rodents address = " << &rodents << endl;

    int bunnies = 50;
    rodents = bunnies;       // can we change the reference?
    cout << "bunnies = " << bunnies;
    cout << ", rats = " << rats;
    cout << ", rodents = " << rodents << endl;

    cout << "bunnies address = " << &bunnies;
    cout << ", rodents address = " << &rodents << endl;

	system("pause");
	return 0;
}

在这里插入图片描述

说明:
(a)
在这里插入图片描述在这里插入图片描述
(b)指针和引用的小eg
在这里插入图片描述

2.将引用用作函数参数(引用用的最主要的方式)

(1)引用传递的含义以及对C语言的超越地方的说明
在这里插入图片描述
(2)按值传递和按指针传递的区别:
在这里插入图片描述

(3)交换两个变量的值,引用传递,使用指针和值传递之间的区别
在这里插入图片描述
在这里插入图片描述

具体eg如下所示:

#include<stdio.h>
#include<stdlib.h>


// swaps.cpp -- swapping with references and with pointers
#include <iostream>
void swapr(int & a, int & b);   // a, b are aliases for ints
void swapp(int * p, int * q);   // p, q are addresses of ints
void swapv(int a, int b);       // a, b are new variables
int main()
{
    using namespace std;
    int wallet1 = 300;
    int wallet2 = 350;

    cout << "wallet1 = $" << wallet1;
    cout << " wallet2 = $" << wallet2 << endl;

    cout << "Using references to swap contents:\n";
    swapr(wallet1, wallet2);   // pass variables
    cout << "wallet1 = $" << wallet1;
    cout << " wallet2 = $" << wallet2 << endl;

    cout << "Using pointers to swap contents again:\n";
    swapp(&wallet1, &wallet2); // pass addresses of variables
    cout << "wallet1 = $" << wallet1;
    cout << " wallet2 = $" << wallet2 << endl;

    cout << "Trying to use passing by value:\n";
    swapv(wallet1, wallet2);   // pass values of variables
    cout << "wallet1 = $" << wallet1;
    cout << " wallet2 = $" << wallet2 << endl;

	system("pause");
	return 0;
}

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;
}

void swapv(int a, int b)        // try using values
{
    int temp;

    temp = a;      // use a, b for values of variables
    a = b;
    b = temp; 
}


在这里插入图片描述

说明:
(a)引用传递和指针都成功的交换了两个wallet的内容,而值传递的方法没能完成这项任务。
(b)引用传递与值传递的区别如下:
在这里插入图片描述
(c)引用传递和指针传递的区别如下:
在这里插入图片描述

3.引用的属性和特别之处

(1)eg
在这里插入图片描述

#include<stdio.h>
#include<stdlib.h>


// cubes.cpp -- regular and reference arguments
#include <iostream>
double cube(double a);
double refcube(double &ra);
int main ()
{
    using namespace std;
    double x = 3.0;

    cout << cube(x);
    cout << " = cube of " << x << endl;
    cout << refcube(x);
    cout << " = cube of " << x << endl;
    // cin.get();

	system("pause");
	return 0;
}


double cube(double a)
{
    a *= a * a;
    return a;
}

double refcube(double &ra)
{
    ra *= ra * ra;
    return ra; 
}

在这里插入图片描述
说明:
(a)在这里插入图片描述

(b)啥时候用值传递,啥时候用指针传递?
在这里插入图片描述
(c)传递引用限制更加严格
在这里插入图片描述
(d)如果写成如下的形式,注意体会在形参中使用const的感觉

#include<iostream>
#include<stdlib.h>
#include<stdio.h>


// cubes.cpp -- regular and reference arguments
#include <iostream>
double cube(double a);
double refcube(const double &ra);
int main()
{
    using namespace std;
    double x = 3.0;
    //double re;


    cout << cube(x);
    cout << " = cube of " << x << endl;
    double re=refcube(x);
    cout << re;

    cout << " = cube of "  <<x<< endl;
    // cin.get();
    cout<<"shazi"<<endl;

	//system("pause");
	return 0;
}


double cube(double a)
{
    a *= a * a;
    return a;
}

double refcube(const double &ra)
{
    double res;
    res=ra* ra * ra;
    return res;
}

在这里插入图片描述

(2)临时变量、引用、const
(a)如果实参与引用参数不匹配,C++将生成临时变量
(b)什么时候创建临时变量呢?
在这里插入图片描述
(c)什么是左值,非左值?
在这里插入图片描述
(d)eg:在这里插入图片描述
在这里插入图片描述
说明:
在这里插入图片描述

(e)在这里插入图片描述

(f)再次强调一下使用const的好处:尽可能地将引用声明为const

在这里插入图片描述

4.将引用用于结构

(1)使用引用主要是为了用于结构和类这些类型的,而不是基本的内置类型。
(2)在结构中如何使用引用?
在这里插入图片描述
eg1:
在这里插入图片描述

eg2:

#include<stdio.h>
#include<stdlib.h>


//strc_ref.cpp -- using structure references
#include <iostream>
#include <string>
struct free_throws
{
    std::string name;
    int made;
    int attempts;
    float percent;
};

void display(const free_throws & ft);
void set_pc(free_throws & ft);
free_throws & accumulate(free_throws &target, const free_throws &source);

int main()
{
    free_throws one = {"Ifelsa Branch", 13, 14};
    free_throws two = {"Andor Knott", 10, 16};
    free_throws three = {"Minnie Max", 7, 9};
    free_throws four = {"Whily Looper", 5, 9};
    free_throws five = {"Long Long", 6, 14};
    free_throws team = {"Throwgoods", 0, 0};
    free_throws dup;
    set_pc(one);
    display(one);
    accumulate(team, one);
    display(team);
// use return value as argument
    display(accumulate(team, two));
    accumulate(accumulate(team, three), four);
    display(team);
// use return value in assignment
    dup = accumulate(team,five);
    std::cout << "Displaying team:\n";
    display(team);
    std::cout << "Displaying dup after assignment:\n";
    display(dup);
    set_pc(four);
// ill-advised assignment
    accumulate(dup,five) = four;
    std::cout << "Displaying dup after ill-advised assignment:\n";
    display(dup);
    // std::cin.get();

	system("pause");
	return 0;
}



void display(const free_throws & ft)
{
    using std::cout;
    cout << "Name: " << ft.name << '\n';
    cout << "  Made: " << ft.made << '\t';
    cout << "Attempts: " << ft.attempts << '\t';
    cout << "Percent: " << ft.percent << '\n';
}
void set_pc(free_throws & ft)
{
    if (ft.attempts != 0)
        ft.percent = 100.0f *float(ft.made)/float(ft.attempts);
    else
        ft.percent = 0;
}

free_throws & accumulate(free_throws & target, const free_throws & source)
{
    target.attempts += source.attempts;
    target.made += source.made;
    set_pc(target);
    return target;
}

在这里插入图片描述

说明:
(a)在初始化多个结构对象时,如果指定的初始值比成员少,则余下的成员(这里的percent)将被设置为0.
(b)对于set_pc()函数而言
在这里插入图片描述在这里插入图片描述
(c)对于display()函数而言
在这里插入图片描述
(d)对于accumulate()函数而言
在这里插入图片描述
(e)对于返回值是引用而言的说明如下,
在这里插入图片描述
具体意思就是:
在这里插入图片描述
在这里插入图片描述
(f)为何要返回引用??
在这里插入图片描述
(j)返回引用时需要注意的问题
错误的示例:返回临时变量
在这里插入图片描述
正确的方法1
在这里插入图片描述

正确的方法2
在这里插入图片描述
在这里插入图片描述
(h)为何将const用于引用返回类型?
第一点:
在这里插入图片描述
第二点:
在这里插入图片描述

5.将引用用于类对象

(1)通过使用引用,可以让函数将类string、ostream、istream、ofstream和ifstream等类的对象作为参数。
(2)eg的基本思想是:使用string类,创建一个函数,它将指定的字符串加入到另一个字符串的前面和后面。

#include<stdio.h>
#include<stdlib.h>


// strquote.cpp  -- different designs
#include <iostream>
#include <string>
using namespace std;
string version1(const string & s1, const string & s2);
const string & version2(string & s1, const string & s2);  // has side effect
const string & version3(string & s1, const string & s2);  // bad design
 
int main()
{
    string input;
    string copy;
    string result;

    cout << "Enter a string: ";
    getline(cin, input);
    copy = input;
    cout << "Your string as entered: " << input << endl;
    result = version1(input, "***");
    cout << "Your string enhanced: " << result << endl;
    cout << "Your original string: " << input << endl;
 
    result = version2(input, "###");
    cout << "Your string enhanced: " << result << endl;
    cout << "Your original string: " << input << endl;

    cout << "Resetting original string.\n";
    input = copy;
    result = version3(input, "@@@");
    cout << "Your string enhanced: " << result << endl;
    cout << "Your original string: " << input << endl;

	system("pause");
	return 0;
}



string version1(const string & s1, const string & s2)
{
    string temp;

    temp = s2 + s1 + s2;
    return temp;
}

const string & version2(string & s1, const string & s2)   // has side effect
{
    s1 = s2 + s1 + s2;
// safe to return reference passed to function
    return s1; 
}

const string & version3(string & s1, const string & s2)   // bad design
{
    string temp;

    temp = s2 + s1 + s2;
// unsafe to return reference to local variable
    return temp;
}

在这里插入图片描述
说明:
(a)将C语言风格的字符串用作string对象引用参数的说明
在这里插入图片描述
在这里插入图片描述

(b)对于第一个version1而言,
在这里插入图片描述
(c)对于第二个version2而言,
在这里插入图片描述
在这里插入图片描述

(d)对于第三个version3而言,不要对临时变量返回引用

在这里插入图片描述

6.对象、继承和引用

(1)ofstream对象可以使用ostream类的方法,这使得文件输入/输出的格式与控制台输入/输出相同
(2)继承的特征1:
继承:使得能够将特性从一个类传递给另一个类的语言特性。
eg:
在这里插入图片描述
(3)继承的特征2:
在这里插入图片描述在这里插入图片描述

(4)eg:
在这里插入图片描述

#include<stdio.h>
#include<stdlib.h>


//filefunc.cpp -- function with ostream & parameter
#include <iostream>
#include <fstream>
#include <cstdlib>
using namespace std;

void file_it(ostream & os, double fo, const double fe[],int n);
const int LIMIT = 5;
int main()
{
    ofstream fout;
    const char * fn = "ep-data.txt";
    fout.open(fn);
    if (!fout.is_open())
    {
        cout << "Can't open " << fn << ". Bye.\n";
        exit(EXIT_FAILURE);
    }
    double objective;
    cout << "Enter the focal length of your "
            "telescope objective in mm: ";
    cin >> objective;
    double eps[LIMIT];
    cout << "Enter the focal lengths, in mm, of " << LIMIT
         << " eyepieces:\n";
    for (int i = 0; i < LIMIT; i++)
    {
        cout << "Eyepiece #" << i + 1 << ": ";
        cin >> eps[i];
    }
    file_it(fout, objective, eps, LIMIT);
    file_it(cout, objective, eps, LIMIT);
    cout << "Done\n";

	system("pause");
	return 0;
}



void file_it(ostream & os, double fo, const double fe[],int n)
{
    // save initial formatting state
    ios_base::fmtflags initial;
    initial = os.setf(ios_base::fixed, ios_base::floatfield);
    std::streamsize sz = os.precision(0);
    os << "Focal length of objective: " << fo << " mm\n";
    os.precision(1);
    os.width(12);//可以不要
    os << "f.l. eyepiece";
    os.width(15);//可以不要
    os << "magnification" << endl;
    for (int i = 0; i < n; i++)
    {
        os.width(12);
        os << fe[i];
        os.width(15);
        os << int (fo/fe[i] + 0.5) << endl;
    }
    // restore initial formatting state
    os.setf(initial, ios_base::floatfield);
    os.precision(sz);
}

在这里插入图片描述
说明:
(a)参数os(类型是ostream &)可以指向ostream对象(如cout),也可以指向ofstream对象(如fout)。
(b)使用ostream类中的格式化方法
setf()能够设置各种格式化状态:
setf(ios_base::fixed)将对象置于使用定点表示;
setf(ios_base::showpoint)将对象置于显示小数点的模式,即小数部分为0;
precision()指定显示多少位小数(假设对象处于定点模式下);
width()设置下一次输出操作使用的字段宽度,这种设置只在显示下一个值时有效,然后就恢复到默认设置(默认为0);
所有这些设置都将一直保持不变,直到再次调用相应的方法重新设置它们

(c)在这里插入图片描述
在这里插入图片描述

7.何时使用引用参数??

在这里插入图片描述

三、默认参数

(1)默认参数指的是:当函数调用中省略了实参时,自动使用的一个值。
(2)如何设置默认值??
在这里插入图片描述
(3)对于带参数列表的函数而言
在这里插入图片描述
(4)对于实参而言
在这里插入图片描述
(5)默认参数的好处
在这里插入图片描述

(6)具体eg:在这里插入图片描述

#include<stdio.h>
#include<stdlib.h>


// left.cpp -- string function with a default argument
#include <iostream>
const int ArSize = 80;
char * left(const char * str, int n = 1);
int main()
{
    using namespace std;
    char sample[ArSize];
    cout << "Enter a string:\n";
    cin.get(sample,ArSize);
    char *ps = left(sample, 4);
    cout << ps << endl;
    delete [] ps;       // free old string
    ps = left(sample);
    cout << ps << endl;
    delete [] ps;       // free new string

	system("pause");
	return 0;
}


// This function returns a pointer to a new string
// consisting of the first n characters in the str string.
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; 
}

在这里插入图片描述

说明:
(a)第一次用的是实参的参数,第二次使用的是默认参数,而默认参数在函数声明那里
在这里插入图片描述
(b)在函数char * left()中,很细节的考虑到了三个地方,并且用while循环来给新的字符串加结尾\0
在这里插入图片描述
(c)更进一步地,可以设置一种新字符串长度
在这里插入图片描述

(d)但是作为C语言程序员以及C++程序员,还可以做的更好,C程序员用strlen,C++程序员就会用比strlen更好的方法。
在这里插入图片描述

四、函数重载

(1)默认参数与函数多态(函数重载)的区别是:
默认参数让您能够使用不同数目的参数调用同一个函数;
**函数多态(函数重载)**让您能够使用多个同名的函数,即完成相同的工作,但是使用不同的参数列表。

(2)函数重载的关键是函数的参数列表——也称之为函数特征标(function signature)。C++允许定义名称相同的函数,条件是:它们的特征标不同,如果参数数目和或者参数类型不同,则特征标也不同。

(3)eg:
在这里插入图片描述
(4)有些不同的特征标是不能共存的:引用和类型本身视为同一个特征标
在这里插入图片描述

(5)
在这里插入图片描述
说明:
(a)dribble()函数有两个原型,一个用于const指针,另一个用于常规指针。
dribble()函数只与带非const参数的调用匹配;
drivel()函数可以与带const或者不带const参数的调用匹配
在这里插入图片描述

(6)
在这里插入图片描述

(7)对于重载引用参数而言,类设计和STL会经常用到
在这里插入图片描述

1.函数重载的例子

(1)前面写了一个left()函数,它返回了一个指针,指向字符串的前n个字符。
在这里,再添加另一个left()函数,让其返回整数的前n位。

(2)编写的要点:
首先,先计算数字包含多少位。将数字除以10便可以去掉一位,因此,可以使用出发来计算数位
在这里插入图片描述
在这里插入图片描述
其次,是要删除多少位
在这里插入图片描述

(3)最终,需要编写的函数的要求:由于考虑的多,所以最终的结果很完美
在这里插入图片描述

#include<stdio.h>
#include<stdlib.h>


// leftover.cpp -- overloading the left() function
#include <iostream>
unsigned long left(unsigned long num, unsigned ct);
char * left(const char * str, int n = 1);

int main()
{
     using namespace std;
    char * trip = "Hawaii!!";   // test value
    unsigned long n = 12345678; // test value
    int i;
    char * temp;

    for (i = 1; i < 10; i++)
    {
        cout << left(n, i) << endl;
        temp = left(trip,i);
        cout << temp << endl;
        delete [] temp; // point to temporary storage
    }

	system("pause");
	return 0;
}


// This function returns the first ct digits of the number num.
unsigned long left(unsigned long num, unsigned ct)
{
    unsigned digits = 1;
    unsigned long n = num;

    if (ct == 0 || num == 0)
        return 0;       // return 0 if no digits
    while (n /= 10)
        digits++;//总共有多少位digits
    if (digits > ct)
    {
    ct = digits - ct;
    while (ct--)
        num /= 10;
    return num;         // return left ct digits
    }
    else                // if ct >= number of digits
        return num;     // return the whole number
}

// This function returns a pointer to a new string
// consisting of the first n characters in the str string.
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; 
}

在这里插入图片描述

2.何时使用函数重载

(1)在这里插入图片描述
(2)
在这里插入图片描述

五、函数模板

1.函数模板的基本知识

(1)C++新增的一项特性——函数模板
使用泛型来定义函数,其中的泛型可用具体的类型(如int或double)替换。
通过将类型作为参数传递给模板,可使编译器生成该类型的函数
由于模板允许以泛型(不是具体类型) 的方式编写程序,因此有时候也被称之为:通用编程。

(2)C++模板的到底是干啥的??
在这里插入图片描述

(3)举例一个函数模板的例子eg如下:在这里插入图片描述
说明:
(a)
在这里插入图片描述
(b)使用模板的作用是:
在这里插入图片描述

(4)再举例一个函数模板的例子eg如下:在这里插入图片描述
在这里插入图片描述

(5)函数模板在哪用?
在这里插入图片描述

(6)举个eg:
在这里插入图片描述

#include<stdio.h>
#include<stdlib.h>


// funtemp.cpp -- using a function template
#include <iostream>
// function template prototype
template <typename T>  // or class T
void Swap(T &a, T &b);

int main()
{
    using namespace std;
    int i = 10;
    int j = 20;
    cout << "i, j = " << i << ", " << j << ".\n";
    cout << "Using compiler-generated int swapper:\n";
    Swap(i,j);  // generates void Swap(int &, int &)
    cout << "Now i, j = " << i << ", " << j << ".\n";

    double x = 24.5;
    double y = 81.7;
    cout << "x, y = " << x << ", " << y << ".\n";
    cout << "Using compiler-generated double swapper:\n";
    Swap(x,y);  // generates void Swap(double &, double &)
    cout << "Now x, y = " << x << ", " << y << ".\n";
    // cin.get();

	system("pause");
	return 0;
}




// function template definition
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; 
}

在这里插入图片描述

说明:
(a)用int替换了所有的T
在这里插入图片描述
(b)同理,用double替换了T
在这里插入图片描述

(3)使用模板的好处:使得生成的多个函数定义更简单、更可靠。
更常见的情况是,将模板放在头文件中,并在需要使用 模板的文件中包含头文件。

2.重载模板

(1)重载模板的特点:
由于并非所有的类型都使用相同的算法(函数模板的特点),可以像重载函数那样定义重载模板。
要求:被重载模板的函数特征标必须不同。
eg下面的例子:
特点:
在这里插入图片描述

#include<stdio.h>
#include<stdlib.h>


// twotemps.cpp -- using overloaded template functions
#include <iostream>
template <typename T>     // original template
void Swap(T &a, T &b);

template <typename T>     // new template
void Swap(T *a, T *b, int n);

void Show(int a[]);
const int Lim = 8;
int main()
{
    using namespace std;
    int i = 10, j = 20;
    cout << "i, j = " << i << ", " << j << ".\n";
    cout << "Using compiler-generated int swapper:\n";
    Swap(i,j);              // matches original template
    cout << "Now i, j = " << i << ", " << j << ".\n";

    int d1[Lim] = {0,7,0,4,1,7,7,6};
    int d2[Lim] = {0,7,2,0,1,9,6,9};
    cout << "Original arrays:\n";
    Show(d1); 
    Show(d2);
    Swap(d1,d2,Lim);        // matches new template
    cout << "Swapped arrays:\n";
    Show(d1);
    Show(d2);
    // cin.get();

	system("pause");
	return 0;
}


template <typename T>
void Swap(T &a, T &b) 
{
    T temp;
    temp = a;
    a = b;
    b = temp;
}

template <typename T>
void Swap(T a[], T b[], int n)
{
    T temp;
    for (int i = 0; i < n; i++)
    {
        temp = a[i];
        a[i] = b[i];
        b[i] = temp;
    }
}

void Show(int a[])
{
    using namespace std;
    cout << a[0] << a[1] << "/";
    cout << a[2] << a[3] << "/";
    for (int i = 4; i < Lim; i++)
        cout << a[i];
    cout << endl;
}

在这里插入图片描述

3.模板的局限性

在这里插入图片描述

4.显示具体化

(1)显示具体化解决了上述所谓的模板局限性
在这里插入图片描述

(2)重载模板的匹配顺序
在这里插入图片描述
eg:
在这里插入图片描述
在这里插入图片描述

(3)举一个显示具体化的eg,如下:

#include<stdio.h>
#include<stdlib.h>


// twoswap.cpp -- specialization overrides a template
#include <iostream>
template <typename T>
void Swap(T &a, T &b);

struct job
{
    char name[40];
    double salary;
    int floor;
};

// explicit specialization 
template <> void Swap<job>(job &j1, job &j2);
void Show(job &j);

int main()
{
    using namespace std;
    cout.precision(2);
    cout.setf(ios::fixed, ios::floatfield);
    int i = 10, j = 20;
    cout << "i, j = " << i << ", " << j << ".\n";
    cout << "Using compiler-generated int swapper:\n";
    Swap(i,j);    // generates void Swap(int &, int &)
    cout << "Now i, j = " << i << ", " << j << ".\n";

    job sue = {"Susan Yaffee", 73000.60, 7};
    job sidney = {"Sidney Taffee", 78060.72, 9};
    cout << "Before job swapping:\n";
    Show(sue);
    Show(sidney);
    Swap(sue, sidney); // uses void Swap(job &, job &)
    cout << "After job swapping:\n";
    Show(sue);
    Show(sidney);

	system("pause");
	return 0;
}


template <typename T>
void Swap(T &a, T &b)    // general version
{
    T temp;
    temp = a;
    a = b;
    b = temp;
}

// swaps just the salary and floor fields of a job structure

template <> void Swap<job>(job &j1, job &j2)  // specialization
{
    double t1;
    int t2;
    t1 = j1.salary;
    j1.salary = j2.salary;
    j2.salary = t1;
    t2 = j1.floor;
    j1.floor = j2.floor;
    j2.floor = t2;
}

void Show(job &j)
{
    using namespace std;
    cout << j.name << ": $" << j.salary
         << " on floor " << j.floor << endl;
}

在这里插入图片描述
说明:
(a)cout中对精度的控制怎么写??
在这里插入图片描述
(b)Swap(i,j)匹配的是
在这里插入图片描述
Swap(sue,sidney)匹配的是
在这里插入图片描述
(c)这个模板具体化在这里插入图片描述
(d)show()函数的形参中,定义了一个引用
在这里插入图片描述

5.实例化和具体化

(1)显示实例化和隐式实例化
(a)我们知道,编译器使用模板为特定类型生成函数定义时,得到的是模板实例(instantiation)
隐式实例化
在这里插入图片描述
eg:
template
void Swap(T &a, T &b) // general version
{
T temp;
temp = a;
a = b;
b = temp;
}

显式实例化
在这里插入图片描述
(b)显示具体化
在这里插入图片描述

(c)显示具体化和显示实例化的区别:
在这里插入图片描述
注意:
在这里插入图片描述

(2)
在这里插入图片描述
eg:对于其它的Swap()调用,编译器将根据函数调用时,实际使用的参数生成相应的版本。
在这里插入图片描述

6.编译器选择使用哪个函数版本

(1)重载解析及其过程
在这里插入图片描述

(2)理解重载解析的很重要的例子
eg:考虑只有一个函数参数的情况,如下面的调用:
在这里插入图片描述
首先编译器将寻找候选者, 即名称为may()的函数和函数模板(注意:只考虑特征标,不考虑返回类型)。然后寻找那些可以用一个参数调用的函数。
eg:在这里插入图片描述
在这里插入图片描述
说明:把字符‘a’当作整型,在这里插入图片描述
接下来,
在这里插入图片描述
结果是:
函数3,函数5>函数6>函数1>函数2
在这里插入图片描述

(3)完全匹配和最佳匹配
在这里插入图片描述
eg:
在这里插入图片描述
说明:
(a)情况1:不能完全匹配,编译器可能会提示的错误是:
在这里插入图片描述
(b)情况2:在这里插入图片描述
(c)情况3:
在这里插入图片描述
eg1:
在这里插入图片描述

eg2:最具体的含义
用于找出最具体的模板的规则被称之为:函数模板的部分排序规则
在这里插入图片描述

(4)部分排序规则的eg如下:
在这里插入图片描述

#include<stdio.h>
#include<stdlib.h>


// tempover.cpp --- template overloading
#include <iostream>

template <typename T>            // template A
void ShowArray(T arr[], int n);

template <typename T>            // template B
void ShowArray(T * arr[], int n);

struct debts
{
    char name[50];
    double amount;
};

int main()
{
    using namespace std;
    int things[6] = {13, 31, 103, 301, 310, 130};
    
	//结构体数组
	struct debts mr_E[3] =
    {
        {"Ima Wolfe", 2400.0},
        {"Ura Foxe", 1300.0},
        {"Iby Stout", 1800.0}
    };
	
	//指针数组
    double * pd[3]; 

// set pointers to the amount members of the structures in mr_E
    for (int i = 0; i < 3; i++)
        pd[i] = &mr_E[i].amount;
    
    cout << "Listing Mr. E's counts of things:\n";
// things is an array of int
    ShowArray(things, 6);  // uses template A
    cout << "Listing Mr. E's debts:\n";
// pd is an array of pointers to double
    ShowArray(pd, 3);      // uses template B (more specialized)
    // cin.get();

	system("pause");
	return 0;
}



template <typename T>
void ShowArray(T arr[], int n)
{
    using namespace std;
    cout << "template A\n";
    for (int i = 0; i < n; i++)
        cout << arr[i] << ' ';
    cout << endl;
}

template <typename T>
void ShowArray(T * arr[], int n)
{
    using namespace std;
    cout << "template B\n";
    for (int i = 0; i < n; i++)
        cout << *arr[i] << ' ';
    cout << endl; 
}

在这里插入图片描述
说明:
(a) ShowArray(things, 6); // uses template A
在这里插入图片描述
(b)ShowArray(pd, 3); // uses template B (more specialized)
与模板A匹配的话,输出地址的原因是cout<<arr[i]<< ’ ’
在这里插入图片描述

与模板B匹配的话
在这里插入图片描述

(c)
在这里插入图片描述
在这里插入图片描述

(5)自己选择
在这里插入图片描述

#include<stdio.h>
#include<stdlib.h>


// choices.cpp -- choosing a template
#include <iostream>

template<class T>
T lesser(T a, T b)         // #1
{
    return a < b ? a : b;
}

int lesser (int a, int b)  // #2
{
    a = a < 0 ? -a : a;
    b = b < 0 ? -b : b;
    return a < b ? a : b;
}

int main()
{
    using namespace std;
    int m = 20;
    int n = -30;
    double x = 15.5;
    double y = 25.9;

    cout << lesser(m, n) << endl;       // use #2
    cout << lesser(x, y) << endl;       // use #1 with double
    cout << lesser<>(m, n) << endl;     // use #1 with int
    cout << lesser<int>(x, y)  << endl; // use #1 with int

    // cin.get();

	system("pause");
	return 0;
}

在这里插入图片描述
说明:
(a)先看lesser(m, n)和lesser(x, y) 的情况
在这里插入图片描述
(b)再看lesser<>(m, n) 和 lesser< int >(x, y) 的情况
在这里插入图片描述

(6)多个参数的函数
在这里插入图片描述

7.函数模板的发展(了解即可,decltype关键字实在看得太少了)

(1)是什么类型??
在这里插入图片描述
(2)C++11的关键字decltype
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
(3)另一种函数声明语法(C++11后置返回类型
在这里插入图片描述
在这里插入图片描述

六、总结

在这里插入图片描述

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

喜欢打篮球的普通人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值