C++第6版学习小结之第八章:函数探幽

  c++提供了许多新的函数特性,包括内联函数、按引用传递变量、默认的参数值、函数重载(多态)以及模板函数。

c++内联函数

  1. 内联函数是c++为提高程序运行速度所做的一项改进。常规函数和内联函数之间的主要区别不在于编程方式,而在于c++编译器如何将它们组合到程序中。

  2. 编译过程的最终产品是可执行程序----由一组机器语言指令组成。运行程序时,操作系统将这些指令载入到计算机内存中,因此每条指令都有特定内存地址。计算机随后将逐步执行这些指令。有时将跳过一些指令,向前或向后跳到特定地址。

  3. c++内联函数提供了另一种选择。内联函数的编译代码与其他程序代码“内联”起来了。也就是说,编译器将使用相应的函数代码替换函数调用。对于内联代码,程序无需跳到另一个位置处执行代码,再跳回来。因此,内联函数的运行速度比常规函数稍快,但代价是需要占用更多的内存。

  4. 要使用这项特性,必须采取下述措施之一:
    i 在函数声明前加上关键字inline;
    ii 在函数定义前加上关键字inline。
    通常的做法是省略原型,将整个定义(即函数头和所有函数代码)放在本应提供原型的地方。

  5. 程序员请求将函数作为内联函数时,编译器不一定会满足这种要求。它可能认为该函数过大或注意到函数调用了自己(内联函数不能递归),因此不将其作为内联函数;而有些编译器没有启用或实现这种特性。
      内联函数square()(计算参数平方)演示了内联技术。注意到整个函数定义都放在一行中,但并不一定非得这样做。然而,如果函数定义占用多行(假定没有使用冗长的标识符),则将其作为内联函数就不太合适。

inline double square(double x){return x * x}
int main()
{
...
cout << square(c++);
...
}
  1. inline工具是c++新增的特性,c++使用#define来提供宏即内联代码的原始实现。如,下面是一个计算平方的宏:
#define SQUARE(X) X*X 

  这并不是通过传递参数实现的,而是通过文本替换来实现的。这种情况会出现问题。如:

b = SQUARE(4.2 + 4.1);

  结果为b = 4.2 + 4.1 x 4.2 + 4.1
  而不是b =(4.2+4.1)x(4.2+4.1)。
  因此,如果使用了类似函数的功能,应将它们转化为c++内联函数。

引用变量

  1. 引用是已知定义的变量的别名。引用变量的主要用途是用作函数的形参。通过将引用变量作为参数,函数将使用原始数据,而不是其副本。这样除指针之外,引用也为函数处理大型结构提供一种非常方便的途径。
  2. c和c++使用&符号来指示变量的地址。c++赋予&符号另一个定义,将其用来声明引用。如将B作为A变量的别名:
int A;
int & B = A;

  上述引用声明允许将A和B互换—它们指向相同的值和内存单元。其中,&不是地址运算符,而是类型标识符的一部分。就像生命中的char*指的是指向char的指针一样,int &指的是执行int的引用。

  1. 引用看上去就像伪装表示的指针。实际上,引用还是不同于指针。除了表示法不同外,还有其他差别,其中之一是,必须在声明引用时将其初始化,而不能像指针那样,先声明,再赋值。
  2. 引用更接近const指针,必须在创建时进行初始化,一旦与某个变量关联起来,就将一直效忠它。也就是说:
int & Bs = As;
实际上是下列代码的伪装表示:
int * const pr = &As;
  1. 引用经常被用作函数参数,使得函数中的变量名成为调用程序中的变量的别名。这种传递参数的方法称为按引用传递。按引用传递允许被调用的函数能够访问调用函数中的变量。
  2. 引用变量传递时,下面代码不合理
double z = refcube(x + 3.0);
因为表达式x + 3.0不是变量
其中refcube()为接受引用参数的函数
  1. 左值参数是可被引用的数据对象,如变量、数组元素、结构成员、引用和解除引用都是左值。非左值包括字面常量(用引号括起的字符串除外,他们由其地址表示)和包含多项的表达式。
  2. 常规变量和const变量都可视为左值,因为可通过地址访问。常规变量属于可修改的左值,而const变量属于不可修改的左值。
  3. 将引用参数声明为常量数据的引用的理由有三个:
    i 使用const可以避免无意中修改数据的编程错误;
    ii 使用const使函数能够处理const和非const实参,否则将只能接受非const数据;
    iii 使用const引用使函数能够正确生成并使用临时变量。
    因此,应尽可能将引用形参声明为const。
  4. c++11新增了另一种引用----右值引用。这种引用可指向右值,是使用&&声明的:
double && rref = std::sqrt(36.00);
double j = 15.0;
double && jref = 2.0 * j + 18.5;
std::cout << rref << "\n"; //display 6.0
std::cout << jref << "\n"; //display 48.5

  新增右值引用的主要目的是,让库设计人员能够提供有操作更有效的实现。

  1. 引用非常适合用于结构和类(c++的用户定义类型)。
  2. 将引用用于结构
    如有下结构定义:
struct free_throws
{
....
}

  则可以这样编写原型函数,在函数中将指向该结构的应用作为参数:

void set_pc(free_throws & ft);
如果不希望函数参数修改传入的结构,可使用constvoid set_pc(const free_throws & ft);
  1. 为何要返回引用:
void set_pc(free_throws & ft);
free_throws & accumulate(free_throws & A, const free_throws & B);
struct free_throws
{
	std::string name;
	int made;
	int attempts;
	float percent;
}
void main()
{
	free_throws team = {"MMD", 0, 0};
	free_throws five = {"hello", 23, 10};
	...
}
void set_pc(free_throws & ft)
{}
free_throws & accumulate(free_throws & A, const free_throws & B)
{
	A.attempts += B.attempts;
	A.made += B.made;
	set_pc(A);
	return A;
}
...

  现在看下面的语句:

dup = accumulate(team, five);

  如果accumulate()返回一个结构,而不是结构的引用,将把整个结构复制到一个临时位置,再将这个拷贝复制给dup。但在返回值引用时,将直接把team复制到dup,其效率更高。

  1. 将类对象传递给函数时,c++通常的做法是使用引用。
  2. 假设实参的类型与引用参数类型不匹配,但可被转换为引用类型,程序将创建一个正确类型的临时变量,使用转换后的实参值来初始化它,然后传递一个指向该临时变量的引用。
  3. 使得能够将特性从一个类传递给另一个类的语言特性被称为继承。
  4. 派生类继承了基类的方法,意味着派生类可以使用基类的特性。继承的另一个特征是,基类引用可以指向派生类对象,而无需进行强制类型转换。这种特征的一个实际结果是,可以定义一个接受基类引用作为参数的函数,调用该函数时,可以将基类对象作为参数,也可以将派生类对象作为参数。
  5. 方法setf()让你能够设置各种格式化状态。
  6. 何时使用引用参数:

默认参数

  1. 对于带参数列表的函数,必须从右向左添加默认值。也就是说,要为某个参数设置默认值,则必须为它右边的所有参数提供默认值。
  2. 实参按从左到右的顺序依次被赋给相应的形参,而不能跳过任何参数。

函数重载

  1. 函数重载的关键是函数的参数列表----也称为函数特征标。

函数模板

  1. 函数模板允许以任意类型的方式来定义函数。如下列交换模板:
template <typename AnyType>
void Swap(AnyType &a, AnyType &b)
{
	AnyType temp;
	temp = a;
	a = b; 
	b = temp;
}

  第一行指出,要建立一个模板,并将类型命名为Anytype。关键字template和typename是必须的,除非使用关键字class代替typename。另外,必须使用尖括号。类型名可以任意选择(这里为AnyType),只要遵循c++命名规则即可;许多程序员都是用简单的名称,如T。

  1. 如果需要多个将同一种算法用于不同类型的函数,请使用模板。
  2. 模板的局限性,编写的模板函数很可能无法处理某些类型。
  3. 在代码中包含函数模板本身并不会生成函数定义,它只是一个用于生成函数定义的方案。编译器使用模板为特定类型生成函数定义时,得到的是模板实例。模板并非函数定义,但使用int模板实例是函数定义。这种实例化方式被称为隐式实例化。
  4. 最初,编译器只能通过隐式实例化,来使用模板生成函数定义,但现在c++还允许显示实例化。
  5. 显示具体化声明在关键字template后包含<>,而显示实例化没有。
  6. 试图在同一个文件(或转换单元)中使用同一种类型的显示实例和显示具体化将出错。
  7. 隐式实例化、显示实例化和显示具体化统称为具体化。它们的相同之处在于,它们表示的都是使用具体类型的函数定义,而不是通用描述。
  8. 引入显示实例化后,必须使用新的语法-----在声明中使用前缀template和template<>,以区分显示实例化和显示具体化。
template <> void Swap<job>(job &, job &); //显示具体化
template void Swap<job>(job &, job &); //显示实例化
  1. 关键字decltype:c++11新增的关键字,作用如下
int x;
decltype(x) y; //让y与x类型一致

  decltype提供的参数可以是表达式:

decltype(x + y) z; //让z与x+y类型一致
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值