您的位置:首页 > 编程语言 > C语言/C++

C++primer第五版笔记-第六章函数

2015-10-03 15:47 411 查看
1、局部静态对象

将对象声明成static的作用是在程序的执行路径第一次经过对象定义语句时初始化它,并且直到程序终止才销毁。在此期间即使对象所在的函数结束执行也不会对他有影响。

#include <iostream>

using namespace std;

size_t cout_calls() {
static size_t ctr = 0;//调用结束后,这个值依然有效
return ++ctr;
}

int main() {
for (size_t i = 0; i != 10; ++i) {
cout << cout_calls() << endl;
}
system("pause");
return 0;
}


2、参数传递

2.1、传值参数

指针形参:当执行指针拷贝操作时,拷贝的是指针的值。拷贝之后两个指针是不同的指针。因为指针使我们可以简介的访问它所指的对象。所以通过指针可以修改它所指对象的值。
void reset(int *ip) {
int n = 0,i = 42;
int *p = &n, *q = &i;//p指向n,q指向i
*p = 42;             //n的值改变,p不变
p = q;               //p现在指向了i,但是i和 n的值都不改变

*ip = 0;             //只改变指针ip所指向对象的值
ip = 0;              //只改变了ip的局部拷贝,实参维改变
}
int main() {
int i = 50;
reset(&i);           //改变i的值而非i的地址
cout << i << endl;//输出0
system("pause");
return 0;
}


2.2、传引用参数

void reset(int &ip) {//ip是传给函数的对象的另一个名字
int n = 0,i = 42;
int &r = n;        //r绑定了n(即r是n的另一个名字)
r = 42;            //现在n的值为42
r = i;             //现在n的值和i相同
i = r;             //i的值和n相同

ip = 0;
}

int main() {
int i = 50;
reset(i);           //改变i的值而非i的地址
cout << i << endl;
system("pause");
return 0;
}


注意:如果类型较大,应该使用引用避免拷贝;如果函数无需改变引用形参的值,最好将其声明为常量引用
bool isShorter(const string &s1, const string &s2) {
return s1.size() < s2.size();

}


返回额外信息
//size_type 与机器无关,而int与机器有关
string::size_type find_char(const string &s, char c, string::size_type &occurs) {
auto ret = s.size();   //第一次出现的位置
occurs = 0;            //出现次数
for (decltype(ret) i = 0; i != s.size(); ++i) {
if (s[i] == c) {
if (ret == s.size())
ret = i;    //记录c第一次出现的位置
++occurs;       //将出现的次数加1
}
}
return ret;
}


const形参和实参

const int ci = 42;//不能改变ci,const是顶层的
int i = ci;       //正确,当拷贝ci时,忽略了他顶层的ci
int * const p = & i;//const是顶层的,不能给p赋值
*p = 0;              //正确,通过p改变对象的内容是允许的,现在i变成了0


尽量使用常量引用
//不良设计,第一个形参类型应该是const string&
string::size_type find_char(const string &s, char c, string::size_type &occurs) {
auto ret = s.size();   //第一次出现的位置
occurs = 0;            //出现次数
for (decltype(ret) i = 0; i != s.size(); ++i) {
if (s[i] == c) {
if (ret == s.size())
ret = i;    //记录c第一次出现的位置
++occurs;       //将出现的次数加1
}
}
return ret;

}

bool is_sentences(const string &s) {
//如果s的末尾有一个句号,则s是一个句子
string::size_type ctr = 0;
return find_char(s, '.', ctr) == s.size() - 1 && ctr == 1;
}


数组形参:不允许拷贝数组;使用数组时将其转换为指针
<pre name="code" class="cpp">//以下三个等价
void print(const int*);
void print(const int[]);
void print(const int[10]); //这里维度表示我们期望含有多少个元素,实际不一定

int main() {
int i = 0; j[2] = { 0,1 };
print(&i);             //正确,&i的类型是int *
print(j);              //正确,j转换成int *并指向j[0]
system("pause");
return 0;
}

//使用标准库规范
void print(const int *beg,const int *end) {
//输出beg到end之间的所有元素
while (beg != end)
{
cout << *beg++ << " ";
}
cout << endl;
}

//显示传递一个数组大小的形参,其中,const int ia[]等价于const int* ia
void print(const int ia[], size_t size) {
for (size_t i = 0; i != size; ++i) {
cout << ia[i] << " ";
}
cout << endl;
}

void print(int(&arr)[10]) {
for (auto elem : arr) {
cout << elem << " ";
}
cout << endl;
}
int main() {
int j[2] = { 0,1 };
print(begin(j),end(j));

int a[] = { 0,1,2,3,4,5 };
print(a,end(a)-begin(a));

int b[] = { 0,1,2,3,4,5,6,7,8,9 };
print(b);
system("pause");
return 0;
}




含有可变形参的函数

3、返回值和return语句
//如果ctr的值大于1,返回word的复数形式
string make_plusral(size_t ctr, const string &word, const string &ending) {
return (ctr > 1) ? word + ending:word;
}

//挑出两个string对象中较短的那个,返回其引用
const string &shorterString(const string &s1, const string &s2) {
return s1.size() <= s2.size() ? s1:s2;
}

引用返回左值
char &get_val(string &str, string::size_type ix) {
return str[ix];
}
int main() {
string s("a value");
cout<<s<<endl;
get_val(s, 0) = 'A';
cout << s << endl;
system("pause");
return 0;
}


返回数组指针:因为数组不能被拷贝,所以函数不能返回数组
返回数组指针的函数形式如下所示:
Type
(*function(parameter _list))[dimension]
如,int (*func(int i))[10]
可以按照如下的定义
Func(inti)表示调用func函数时需要一个int类型的实参
(*func(int
i))意味着我们可以对函数调用的结果执行解引用操作
(*func(int
i))[10]表示解引用func的调用将得到一个大小为10的数组

使用尾置返回类型

auto func(int i)->int(*)[10];
int odd[] = { 1,3,5,7,9 };
int even[] = { 0,2,4,6,8 };
decltype(odd) *arrPtr(int i) {
return (i % 2) ? &odd : &even;
}


4、函数重载

重载函数:1、形参数量和形参类型上有所不同;2、不允许两个函数除了返回类型外其他所有要素都相同。
一个拥有顶层const的形参无法和另一个没有顶层const的形参区分开
Recorde lookup(phone);
Recorde lookup(const phone);//重复声明

另一方面,如果形参是某种类型的指针或引用,则通过区别其指向的是常量对象还是非常量对象可以实现函数的重载,此时const是底层的
Recorde lookup(Account &)      //函数作用于Acoount的引用
Recorde lookup(const Account &)//新函数,作用于常量引用


const string &shorterString(const string &s1, const string &s2) {
return s1.size() <= s2.size() ?s1:s2;
}

string &shorterString(string &s1, string &s2) {
auto &r = shorterString(const_cast<const string &>(s1),
const_cast<const string&>(s2));
return const_cast<string &>(r);
}


5、特殊用途语言特性
1、调用函数一般比直接求等价表达式的值要慢一些,在大多数机器上,一次函数调用其实包含一系列工作:调用前先保存寄存器,并在返回时恢复,可能需要拷贝实参,程序转向一个新的位置继续执行。

2、调试帮助
assert预处理:

assert(expr):
首先对expr求值,如果表达式为假,assert输出信息并终止程序的执行。如果为真,则什么也不做。
assert行为依赖于NDEBUG,如果定义了NDEBUG,则assert什么也不做。
__FILE__存放文件名的字符串字面值
__LINE__
存放当前行号的整形字面值
__TIME__存放文件编译时间的字符串字面值
__DATE__存放文件便日期的字符串字面值

3、函数匹配
函数匹配的第一步是选定本次调用对应的重载函数集,集合中的函数称为候选函数;
第二步考察本次调用提供的实参,然后从候选函数中选出能被这组实参调用的函数,这些函数称为可行函数。

6、函数指针
1、
//比较两个string对象的长度

boollengthCompare(const string &, const string &);
该函数的类型是bool(const string &, const string &),
要想声明一个可以指向该函数的指针,只需要用指针替换掉函数名即可
bool(*pf)(const string &, const string&);//未初始化

使用函数指针:当我们把函数名作为一个值使用时,该函数自动转换为指针。
pf = lengthCompare;//pf指向名为lengthCompare的函数
pf = &lengthCompare;//等值语句,&是可选的

此外还可以直接使用

boolb1 = pf("hello","goodbye");
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: