《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

相关文章

关于C++中的虚拟继承的一些总结(虚拟继承,覆盖,派生,隐藏)

1.为什么要引入虚拟继承 虚拟继承是多重继承中特有的概念。虚拟基类是为解决多重继承而出现的。如:类D继承自类B1、B2,而类B1、B2都继承自类A,因此在类D中两次出现类A中的变量和函数。为了节省内存空间,可以将B1、B2对A的继承定义为虚拟继承,而A就成了虚拟基类。实现的代码如下: class A class B1:public virtual A; class B2:pu

C++对象布局及多态实现探索之内存布局(整理的很多链接)

本文通过观察对象的内存布局,跟踪函数调用的汇编代码。分析了C++对象内存的布局情况,虚函数的执行方式,以及虚继承,等等 文章链接:http://dev.yesky.com/254/2191254.shtml      论C/C++函数间动态内存的传递 (2005-07-30)   当你涉及到C/C++的核心编程的时候,你会无止境地与内存管理打交道。 文章链接:http://dev.yesky

51单片机学习记录———定时器

文章目录 前言一、定时器介绍二、STC89C52定时器资源三、定时器框图四、定时器模式五、定时器相关寄存器六、定时器练习 前言 一个学习嵌入式的小白~ 有问题评论区或私信指出~ 提示:以下是本篇文章正文内容,下面案例可供参考 一、定时器介绍 定时器介绍:51单片机的定时器属于单片机的内部资源,其电路的连接和运转均在单片机内部完成。 定时器作用: 1.用于计数系统,可

C++的模板(八):子系统

平常所见的大部分模板代码,模板所传的参数类型,到了模板里面,或实例化为对象,或嵌入模板内部结构中,或在模板内又派生了子类。不管怎样,最终他们在模板内,直接或间接,都实例化成对象了。 但这不是唯一的用法。试想一下。如果在模板内限制调用参数类型的构造函数会发生什么?参数类的对象在模板内无法构造。他们只能从模板的成员函数传入。模板不保存这些对象或者只保存他们的指针。因为构造函数被分离,这些指针在模板外

问题:第一次世界大战的起止时间是 #其他#学习方法#微信

问题:第一次世界大战的起止时间是 A.1913 ~1918 年 B.1913 ~1918 年 C.1914 ~1918 年 D.1914 ~1919 年 参考答案如图所示

[word] word设置上标快捷键 #学习方法#其他#媒体

word设置上标快捷键 办公中,少不了使用word,这个是大家必备的软件,今天给大家分享word设置上标快捷键,希望在办公中能帮到您! 1、添加上标 在录入一些公式,或者是化学产品时,需要添加上标内容,按下快捷键Ctrl+shift++就能将需要的内容设置为上标符号。 word设置上标快捷键的方法就是以上内容了,需要的小伙伴都可以试一试呢!

Tolua使用笔记(上)

目录   1.准备工作 2.运行例子 01.HelloWorld:在C#中,创建和销毁Lua虚拟机 和 简单调用。 02.ScriptsFromFile:在C#中,对一个lua文件的执行调用 03.CallLuaFunction:在C#中,对lua函数的操作 04.AccessingLuaVariables:在C#中,对lua变量的操作 05.LuaCoroutine:在Lua中,

AssetBundle学习笔记

AssetBundle是unity自定义的资源格式,通过调用引擎的资源打包接口对资源进行打包成.assetbundle格式的资源包。本文介绍了AssetBundle的生成,使用,加载,卸载以及Unity资源更新的一个基本步骤。 目录 1.定义: 2.AssetBundle的生成: 1)设置AssetBundle包的属性——通过编辑器界面 补充:分组策略 2)调用引擎接口API

C++工程编译链接错误汇总VisualStudio

目录 一些小的知识点 make工具 可以使用windows下的事件查看器崩溃的地方 dumpbin工具查看dll是32位还是64位的 _MSC_VER .cc 和.cpp 【VC++目录中的包含目录】 vs 【C/C++常规中的附加包含目录】——头文件所在目录如何怎么添加,添加了以后搜索头文件就会到这些个路径下搜索了 include<> 和 include"" WinMain 和

Javascript高级程序设计(第四版)--学习记录之变量、内存

原始值与引用值 原始值:简单的数据即基础数据类型,按值访问。 引用值:由多个值构成的对象即复杂数据类型,按引用访问。 动态属性 对于引用值而言,可以随时添加、修改和删除其属性和方法。 let person = new Object();person.name = 'Jason';person.age = 42;console.log(person.name,person.age);//'J