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

2024-06-08 07:58

本文主要是介绍《C++ primer plus》学习笔记——第八章函数探幽,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 一、C++内联函数
  • 二、引用变量:C++新增
    • 2.将引用用作函数参数(引用用的最主要的方式)
    • 3.引用的属性和特别之处
      • (f)再次强调一下使用const的好处:尽可能地将引用声明为const
    • 4.将引用用于结构
    • 5.将引用用于类对象
    • 6.对象、继承和引用
    • 7.何时使用引用参数??
  • 三、默认参数
  • 四、函数重载
    • 1.函数重载的例子
    • 2.何时使用函数重载
  • 五、函数模板
    • 1.函数模板的基本知识
    • 2.重载模板
    • 3.模板的局限性
    • 4.显示具体化
    • 5.实例化和具体化
    • 6.编译器选择使用哪个函数版本
    • 7.函数模板的发展(了解即可,decltype关键字实在看得太少了)
  • 六、总结

一、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 expressionscout << "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 referencecout << "rats = " << rats;cout << ", rodents = " << rodents << endl;rodents++;cout << "rats = " << rats;cout << ", rodents = " << rodents << endl;// some implementations require type casting the following
// addresses to type unsignedcout << "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 referencecout << "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 variablescout << "wallet1 = $" << wallet1;cout << " wallet2 = $" << wallet2 << endl;cout << "Using pointers to swap contents again:\n";swapp(&wallet1, &wallet2); // pass addresses of variablescout << "wallet1 = $" << wallet1;cout << " wallet2 = $" << wallet2 << endl;cout << "Trying to use passing by value:\n";swapv(wallet1, wallet2);   // pass values of variablescout << "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 variablesa = 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 variablesa = 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 argumentdisplay(accumulate(team, two));accumulate(accumulate(team, three), four);display(team);
// use return value in assignmentdup = accumulate(team,five);std::cout << "Displaying team:\n";display(team);std::cout << "Displaying dup after assignment:\n";display(dup);set_pc(four);
// ill-advised assignmentaccumulate(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);elseft.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 designint 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 functionreturn s1; 
}const string & version3(string & s1, const string & s2)   // bad design
{string temp;temp = s2 + s1 + s2;
// unsafe to return reference to local variablereturn 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 stateios_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 stateos.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 stringps = left(sample);cout << ps << endl;delete [] ps;       // free new stringsystem("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 characterswhile (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 valueunsigned long n = 12345678; // test valueint 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 digitswhile (n /= 10)digits++;//总共有多少位digitsif (digits > ct){ct = digits - ct;while (ct--)num /= 10;return num;         // return left ct digits}else                // if ct >= number of digitsreturn 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 characterswhile (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 Ttemp = 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 templatecout << "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 templatecout << "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 structuretemplate <> 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_Efor (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 intShowArray(things, 6);  // uses template Acout << "Listing Mr. E's debts:\n";
// pd is an array of pointers to doubleShowArray(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 #2cout << lesser(x, y) << endl;       // use #1 with doublecout << lesser<>(m, n) << endl;     // use #1 with intcout << 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后置返回类型
在这里插入图片描述
在这里插入图片描述

六、总结

在这里插入图片描述

在这里插入图片描述

这篇关于《C++ primer plus》学习笔记——第八章函数探幽的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1041597

相关文章

MySQL高级查询之JOIN、子查询、窗口函数实际案例

《MySQL高级查询之JOIN、子查询、窗口函数实际案例》:本文主要介绍MySQL高级查询之JOIN、子查询、窗口函数实际案例的相关资料,JOIN用于多表关联查询,子查询用于数据筛选和过滤,窗口函... 目录前言1. JOIN(连接查询)1.1 内连接(INNER JOIN)1.2 左连接(LEFT JOI

C++ vector的常见用法超详细讲解

《C++vector的常见用法超详细讲解》:本文主要介绍C++vector的常见用法,包括C++中vector容器的定义、初始化方法、访问元素、常用函数及其时间复杂度,通过代码介绍的非常详细,... 目录1、vector的定义2、vector常用初始化方法1、使编程用花括号直接赋值2、使用圆括号赋值3、ve

MySQL中FIND_IN_SET函数与INSTR函数用法解析

《MySQL中FIND_IN_SET函数与INSTR函数用法解析》:本文主要介绍MySQL中FIND_IN_SET函数与INSTR函数用法解析,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友一... 目录一、功能定义与语法1、FIND_IN_SET函数2、INSTR函数二、本质区别对比三、实际场景案例分

如何高效移除C++关联容器中的元素

《如何高效移除C++关联容器中的元素》关联容器和顺序容器有着很大不同,关联容器中的元素是按照关键字来保存和访问的,而顺序容器中的元素是按它们在容器中的位置来顺序保存和访问的,本文介绍了如何高效移除C+... 目录一、简介二、移除给定位置的元素三、移除与特定键值等价的元素四、移除满足特android定条件的元

利用Python快速搭建Markdown笔记发布系统

《利用Python快速搭建Markdown笔记发布系统》这篇文章主要为大家详细介绍了使用Python生态的成熟工具,在30分钟内搭建一个支持Markdown渲染、分类标签、全文搜索的私有化知识发布系统... 目录引言:为什么要自建知识博客一、技术选型:极简主义开发栈二、系统架构设计三、核心代码实现(分步解析

Python获取C++中返回的char*字段的两种思路

《Python获取C++中返回的char*字段的两种思路》有时候需要获取C++函数中返回来的不定长的char*字符串,本文小编为大家找到了两种解决问题的思路,感兴趣的小伙伴可以跟随小编一起学习一下... 有时候需要获取C++函数中返回来的不定长的char*字符串,目前我找到两种解决问题的思路,具体实现如下:

C++ Sort函数使用场景分析

《C++Sort函数使用场景分析》sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变,如果某些场景需要保持相同元素间的相对顺序,可使... 目录C++ Sort函数详解一、sort函数调用的两种方式二、sort函数使用场景三、sort函数排序

C语言函数递归实际应用举例详解

《C语言函数递归实际应用举例详解》程序调用自身的编程技巧称为递归,递归做为一种算法在程序设计语言中广泛应用,:本文主要介绍C语言函数递归实际应用举例的相关资料,文中通过代码介绍的非常详细,需要的朋... 目录前言一、递归的概念与思想二、递归的限制条件 三、递归的实际应用举例(一)求 n 的阶乘(二)顺序打印

Java调用C++动态库超详细步骤讲解(附源码)

《Java调用C++动态库超详细步骤讲解(附源码)》C语言因其高效和接近硬件的特性,时常会被用在性能要求较高或者需要直接操作硬件的场合,:本文主要介绍Java调用C++动态库的相关资料,文中通过代... 目录一、直接调用C++库第一步:动态库生成(vs2017+qt5.12.10)第二步:Java调用C++

C/C++错误信息处理的常见方法及函数

《C/C++错误信息处理的常见方法及函数》C/C++是两种广泛使用的编程语言,特别是在系统编程、嵌入式开发以及高性能计算领域,:本文主要介绍C/C++错误信息处理的常见方法及函数,文中通过代码介绍... 目录前言1. errno 和 perror()示例:2. strerror()示例:3. perror(