C++学习笔记3
函数小结 :
函数是有名字的计算单元,对程序(就算是小程序)的结构化至关重要。
函数的定义由返回类型、函数名、形參表(可能为空)以及函数体组成。函数体是调用函数时运行的语句块。
在调用函数时,传递给函数的实參必须与对应的形參类型兼容。
给函数传递实參遵循变量初始化的规则。非引用类型的形參以对应实參的副本初始化。对(非引用)形參的不论什么改动仅作用于局部副本。并不影响实參本身。
复制庞大而复杂的值有昂贵的开销。
为了避免传递副本的开销。可将形參指定为引用类型。对引用形參的不论什么改动会直接影响实參本身。应将不须要改动对应实參的引用形參定义为const 引用。
在 C++ 中。函数能够重载。
仅仅要函数中形參的个数或类型不同,则同一个函数名可用于定义不同的函数。编译器将依据函数调用时的实參确定调用哪一个函数。
在重载函数集合中选择适合的函数的过程称为函数匹配。
C++ 提供了两种特殊的函数:内联函数和成员函数。将函数指定为内联是建议编译器在调用点直接把函数代码展开。
内联函数避免了调用函数的代价。成员函数则是身为类成员的函数。
1. 函数不能返回还有一个函数或者内置数组类型。但能够返回指向函数的指针,或指向数组元素的指针的指针:
// ok: pointer tofirst element of the array
int *foo_bar() { /*... */ }
//在定义或声明函数时。没有显式指定返回类型是不合法的:
// error: missingreturn type
test(double v1,double v2) { /* ... */
2.函数形參表
函数形參表能够为空,但不能省略。没有不论什么形參的函数能够用空形參表或含有单个keywordvoid 的形參表来表示。比如,以下关于process 的声明是等价的:
void process() { /*... */ } // implicit void parameter list
void process(void){/* ... */ } // equivalent declaration
形參表由一系列用逗号分隔的參数类型和(可选的)參数名组成。
假设两个參数
具有同样的类型,则其类型必须反复声明:
int manip(int v1, v2){ /* ... */ } // error
int manip(int v1, intv2) { /* ... */ } // ok
參数表中不能出现同名的參数。类似地,局部于函数的变量也不能使用与函数的随意參数同样的名字。參数名是可选的,但在函数定义中,通常全部參数都要命名。
參数必须在命名后才干使用。
3. 參数传递
每次调用函数时,都会又一次创建该函数全部的形參,此时所传递的实參将会初始化相应的形參。 形參的初始化与变量的初始化一样:假设形參具有非引用类型,则复制实參的值,假设形參为引用类型,则它仅仅是实參的别名。
4. 复制实參的局限性
复制实參并非在全部的情况下都适合,不适宜复制实參的情况包含:
• 当须要在函数中改动实參的值时。
• 当须要以大型对象作为实參传递时。对实际的应用而言。复制对象所付出的时间和存储空间代价往往过在。
• 当没有办法实现对象的复制时。
5. 更灵活的指向const 的引用
假设函数具有普通的非const 引用形參,则显然不能通过const 对象进行调用。毕竟。此时函数能够改动传递进来的对象,这样就违背了实參的const 特性。但比較easy忽略的是,调用这种函数时。传递一个右值或具有须要转换的类型的对象相同是不同意的:
// function takes anon-const reference parameter
int incr(int &val)
{
return ++val;
}
int main()
{
short v1 = 0;
const int v2 = 42;
int v3 = incr(v1); // error: v1 is not an int
v3 = incr(v2); // error: v2 is const
v3 = incr(0); // error: literals arenot lvalues
v3 = incr(v1 + v2); // error: addition doesn't yield anlvalue
int v4 = incr(v3); // ok: v3 is a non const objecttype int
}
6. 传递指向指针的引用 –指针的交换
如果我们想编写一个与前面交换两个整数的swap 类似的函数,实现两个指针的交换。已知需用* 定义指针。用& 定义引用。
如今,问题在于怎样将这两个操作符结合起来以获得指向指针的引用。这里给出一个样例:
// swap values of twopointers to int
void ptrswap(int*&v1, int *&v2)
{
int *tmp = v2;
v2 = v1;
v1 = tmp;
}
形參 int *&v1的定义应从右至左理解:v1是一个引用,与指向int 型对象的指针相关联。也就是说,v1仅仅是传递进ptrswap 函数的随意指针的别名。重写第7.2.2 节的main 函数,调用ptrswap 交换分别指向值10 和20 的指针:
int main()
{
int i = 10;
int j = 20;
int *pi = &i; //pi points to i
int *pj = &j; //pj points to j
cout <<"Before ptrswap():\t*pi: " << *pi <<"\t*pj: " << *pj << endl;
ptrswap(pi, pj); //now pi points to j; pj points to i
cout <<"After ptrswap():\t*pi: "<< *pi << "\t*pj: " << *pj<< endl;
return 0;
} // 编译并运行后,该程序产生例如以下结果:
Before ptrswap():*pi: 10 *pj: 20
After ptrswap(): *pi:20 *pj: 10
7. 千万不要返回局部对象的引用
理解返回引用至关重要的是:千万不能返回局部变量的引用。
当函数运行完成时。将释放分配给局部对象的存储空间。
此时,对局部对象的引用就会指向不确定的内存。考虑以下的程序:
// Disaster: Functionreturns a reference to a local object
const string&manip(const string& s)
{
string ret = s;
// transform ret insome way
return ret; // Wrong:Returning reference to a local object!
}
这个函数会在执行时出错,由于它返回了局部对象的引用。当函数执行完成,
字符串ret 占用的储存空间被释放,函数返回值指向了对于这个程序来说不再有效的内存空间。
8. 千万不要返回指向局部对象的指针
函数的返回类型能够是大多数类型。特别地。函数也能够返回指针类型。和返回局部对象的引用一样,返回指向局部对象的指针也是错误的。一旦函数结束,局部对象被释放。返回的指针就变成了指向不再存在的对象的悬垂指针.
9. 内联函数
调用函数比求解等价表达式要慢得多。在大多数的机器上。调用函数都要做非常多工作;调用前要先保存寄存器,并在返回时恢复;复制实參;程序还必须转向一个新位置运行。
将函数指定为inline 函数,(通常)就是将它在程序中每一个调用点上“内联地”展开。
如果我们将shorterString 定义为内联函数。则调用:
cout <<shorterString(s1, s2) << endl;
在编译时将展开为:
cout <<(s1.size() < s2.size() ? s1 : s2) << endl;
从而消除了把shorterString 写成函数的额外运行开销。
从而消除了把shorterString 写成函数的额外运行开销。
// inline version:find longer of two strings inline conststring &
shorterString(conststring &s1, const string &s2)
{
return s1.size() <s2.size() ? s1 : s2;
}
inline 说明对于编译器来说仅仅是一个建议,编译器能够选择忽略这个。
一般来说,内联机制适用于优化小的、仅仅有几行的并且常常被调用的函数。大多数的编译器都不支持递归函数的内联。
一个1200 行的函数也不太可能在调用点内联展开。
内联函数应该在头文件里定义,这一点不同于其它函数。
10. 重载与作用域
一般的作用域规则相同适用于重载函数名。
假设局部地声明一个函数,则该函数将屏蔽而不是重载在外层作用域中声明的同名函数。由此推论。每个版本号的重载函数都应在同一个作用域中声明。
一般来说,局部地声明函数是一种不明智的选择。函数的声明应放在头文件里。
/* Program for illustration purposes only:
* It is bad style fora function to define a local variable
* with the same nameas a global name it wants to use
*/
string init(); // the name init hasglobal scope
void fcn()
{
int init = 0; // init is local andhides global init
string s = init(); // error: global init is hidden
}
…
void print(const string &);
void print(double); // overloads the print function
void fooBar(int ival)
{
void print(int); // new scope: hides previousinstances ofprint
print("Value:"); // error:print(const string &) is hidden
print(ival); //ok: print(int) is visible
print(3.14); //ok: calls print(int); print(double) is hidden
}
11. 候选函数
调用所考虑的重载函数集合,该集合中的函数称为候选函数。候选函数是与被调函数同名的函数,
可行函数
从候选函数中选择一个或多个函数。它们可以用该调用中指定的实參来调用。
因此,选出来的函数称为可行函数.
12. 重载和const 形參
可基于函数的引用形參是指向const 对象还是指向非const 对象,实现函数重载。将引用形參定义为const 来重载函数是合法的,由于编译器能够依据实參是否为const 确定调用哪一个函数:
Recordlookup(Account&);
Record lookup(constAccount&); // new function
const Account a(0);
Account b;
lookup(a); // calls lookup(const Account&)
lookup(b); // calls lookup(Account&)
假设形參是普通的引用。则不能将const 对象传递给这个形參。假设传递了const 对象。则仅仅有带const 引用形參的版本号才是该调用的可行函数。
13. 指向函数的指针
函数指针是指指向函数而非指向对象的指针。
14.函数指针
函数指针类型相当地冗长。使用typedef 为指针类型定义同义词,可将函数指针的使用大大简化:
typedef bool(*cmpFcn)(const string &, const string &);
在引用函数名但又没有调用该函数时,函数名将被自己主动解释为指向函数的指针。
如果有函数:
// compares lengths of two strings
boollengthCompare(const string &, const string &);
除了用作函数调用的左操作数以外,对lengthCompare 的不论什么使用都被解释为例如以下类型的指针:
bool (*)(const string&, const string &);
// 可使用函数名对函数指针做初始化或赋值:
cmpFcn pf1 = 0; // ok: unboundpointer to function
cmpFcn pf2 =lengthCompare; // ok: pointer typematches function's type
pf1 = lengthCompare; // ok: pointer type matchesfunction's type
pf2 = pf1; // ok: pointer types match // 此时。直接引用函数名等效于在函数名上应用取地址操作符:
cmpFcn pf1 =lengthCompare;
cmpFcn pf2 =&lengthCompare;
函数指针仅仅能通过同类型的函数或函数指针或0 值常量表达式进行初始化或赋值。将函数指针初始化为0,表示该指针不指向不论什么函数。指向不同函数类型的指针之间不存在转换:
string::size_typesumLength(const string&, const string&);
boolcstringCompare(char*, char*);
// pointer tofunction returning bool taking two const string& cmpFcn pf;
pf = sumLength; // error: returntype differs
pf = cstringCompare; // error: parameter typesdiffer
pf = lengthCompare; // ok: function and pointertypes match exactly
15. 通过指针调用函数
指向函数的指针可用于调用它所指向的函数。能够不须要使用解引用操作符,直接通过指针调用函数:
cmpFcn pf =lengthCompare;
lengthCompare("hi","bye"); // direct call
pf("hi","bye"); // equivalent call: pf1 implicitly dereferenced
(*pf)("hi","bye"); // equivalent call: pf1 explicitly dereferenced
假设指向函数的指针没有初始化,或者具有0 值。则该指针不能在函数调用中使用。
仅仅有当指针已经初始化,或被赋值为指向某个函数,方能安全地用来调用函数。
16. 指向重载函数的指针
C++ 语言同意使用函数指针指向重载的函数:
extern void ff(vector<double>);
extern void ff(unsigned int); // which functiondoes pf1 refer to?
void (*pf1)(unsignedint) = &ff; // ff(unsigned)
//指针的类型必须与重载函数的一个版本号精确匹配。假设没有精确匹配的函数,则对该指针的初始化或赋值都将导致编译错误:
// error: no match:invalid parameter list
void (*pf2)(int) =&ff; // error: no match:invalid return type
double(*pf3)(vector<double>);
pf3 = &ff;
C++学习笔记3的更多相关文章
- js学习笔记:webpack基础入门(一)
之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...
- PHP-自定义模板-学习笔记
1. 开始 这几天,看了李炎恢老师的<PHP第二季度视频>中的“章节7:创建TPL自定义模板”,做一个学习笔记,通过绘制架构图.UML类图和思维导图,来对加深理解. 2. 整体架构图 ...
- PHP-会员登录与注册例子解析-学习笔记
1.开始 最近开始学习李炎恢老师的<PHP第二季度视频>中的“章节5:使用OOP注册会员”,做一个学习笔记,通过绘制基本页面流程和UML类图,来对加深理解. 2.基本页面流程 3.通过UM ...
- 2014年暑假c#学习笔记目录
2014年暑假c#学习笔记 一.C#编程基础 1. c#编程基础之枚举 2. c#编程基础之函数可变参数 3. c#编程基础之字符串基础 4. c#编程基础之字符串函数 5.c#编程基础之ref.ou ...
- JAVA GUI编程学习笔记目录
2014年暑假JAVA GUI编程学习笔记目录 1.JAVA之GUI编程概述 2.JAVA之GUI编程布局 3.JAVA之GUI编程Frame窗口 4.JAVA之GUI编程事件监听机制 5.JAVA之 ...
- seaJs学习笔记2 – seaJs组建库的使用
原文地址:seaJs学习笔记2 – seaJs组建库的使用 我觉得学习新东西并不是会使用它就够了的,会使用仅仅代表你看懂了,理解了,二不代表你深入了,彻悟了它的精髓. 所以不断的学习将是源源不断. 最 ...
- CSS学习笔记
CSS学习笔记 2016年12月15日整理 CSS基础 Chapter1 在console输入escape("宋体") ENTER 就会出现unicode编码 显示"%u ...
- HTML学习笔记
HTML学习笔记 2016年12月15日整理 Chapter1 URL(scheme://host.domain:port/path/filename) scheme: 定义因特网服务的类型,常见的为 ...
- DirectX Graphics Infrastructure(DXGI):最佳范例 学习笔记
今天要学习的这篇文章写的算是比较早的了,大概在DX11时代就写好了,当时龙书11版看得很潦草,并没有注意这篇文章,现在看12,觉得是跳不过去的一篇文章,地址如下: https://msdn.micro ...
- ucos实时操作系统学习笔记——任务间通信(消息)
ucos另一种任务间通信的机制是消息(mbox),个人感觉是它是queue中只有一个信息的特殊情况,从代码中可以很清楚的看到,因为之前有关于queue的学习笔记,所以一并讲一下mbox.为什么有了qu ...
随机推荐
- Android之TextView------LINK的点击事件
package com.TextHtml; import android.app.Activity; import android.content.Context; import android.os ...
- KindEditor - 代码高亮
*:插入数据库的数据,不用转义,KE已经自动转义过了. 调用的时候使用引入代码的css: 显示代码的页面:
- Xcode使用source control 时提示the server certificate failed to verify 的解决办法
wusipingdeMacBook-Pro:~ railgun$ wusipingdeMacBook-Pro:~ railgun$ svn ls https://13.13.13.134:8443/s ...
- BZOJ 4034: [HAOI2015]T2( 树链剖分 )
树链剖分...子树的树链剖分序必定是一段区间 , 先记录一下就好了 ------------------------------------------------------------------ ...
- 关于PHPExcel类占用内存问题
最近在帮一家公司做后台excel导出功能,使用的工具类是phpexcel,因为这个类功能比较强大.全面. 但是遇到下面一个问题: 当导出数据量达到一定数量级的时候,比如说1000条,服务器出现卡顿.白 ...
- Android Matrix(坐标矩阵)
Android Matrix 2016-02-26 14:38:10 介绍 中文名:坐标矩阵 高等数学里有介绍,在图像处理方面,主要是用于平面的缩放.平移.旋转等操作. 在Android里面,Matr ...
- Observer设计模式【利用商品概念解释】
每个人都想过着富有的生活,这是很正常的. 这里以开店进货为例. 在讲之前解释英语单词: Observer:查看:遵守 Observable:可见的,公开的. 从单词可以知道:商品用来卖,所以公开,继承 ...
- 设计模式 ( 十五 ) 中介者模式Mediator(对象行为型)
设计模式 ( 十五 ) 中介者模式Mediator(对象行为型) 1.概述 在面向对象的软件设计与开发过程中,根据“单一职责原则”,我们应该尽量将对象细化,使其只负责或呈现单一的职责,即将行为分布到各 ...
- 【深圳,武汉】一加科技(One Plus)招聘,寻找不...
[深圳,武汉]一加科技(One Plus)招聘,寻找不... [深圳,武汉]一加科技(One Plus)招聘,寻找不... 来自: 一加 2013-12-30 15:28:04 标题: ...
- [置顶] 自定义java Annotation快速入门与提高
我们先来看看一个简单的注解类定义 import java.lang.annotation.Documented; import java.lang.annotation.Retention; impo ...