C++ Primer第五版学习 第六章

一、基础

1. 函数的定义

返回类型、函数名字、0个或多个形参组成的列表以及函数体。

2. 函数的调用

完成两项工作:用实参初始化函数对应的形参;将控制权转移给被调用的函数。

3. 形参与实参

实参是形参的初始值。实参的类型必须与对应的形参类型相匹配,(能隐式转换也可以)

4.形参列表

每个形参必须有一个声明符

void func() {}    //隐式定义空形参列表
void func(void) {}    //显示定义空形参列表

二、 参数传递

形参的类型决定了形参和实参交互的方式。

如果形参是引用类型,它将绑定到对应的实参上,即引用形参是它对应的实参的别名;

否则,将实参的值拷贝后赋给形参,形参和实参是两个独立的对象。

1. 指针形参

当执行指针拷贝操作时,拷贝的是指针的值。拷贝之后,两个指针是不同的指针。

因为指针可以使我们间接地访问它所指的对象,所以通过指针可以修改它对象的值。

(最好不用指针形参传递需要改变的实参对象,C++中用引用类型的形参更好)

2. 传引用参数

int n = 0,i = 42;
    int& r = n;//r绑定了n(即r是n的另一个名字)
    r = 42;//现在n的值是42
    r = 1;//现在n的值和i相同
    i = r;//i的值和n相同

void reset(int& i) //i是传给reset函数的对象的另一个名字
{
    i = 0;//改变i所引用对象的值
}

int j = 42;
reset(j);//j采用传引用方式,它的值被改变
cout << "j = " << j << endl;//输出j=0

上述调用过程中,形参 i 仅仅是 j 的别名。

使用引用避免拷贝(拷贝比较低效,并且有些类型并不支持拷贝操作)

如果函数无需改变引用形参的值,最好将其声明为常量引用。

3. 形参、局部变量、局部静态变量的区别

形参和函数体内部的变量统称为局部变量,每当函数执行完就会释放;而局部静态变量存储在静态存储区,生命周期贯穿函数调用及之后的时间。

4. 局部静态变量

某些时候,有必要令局部变量的生命周期贯穿函数调用及之后的时间。可以将局部变量定义成static类型从而获得这样的对象。

局部静态变量在程序执行路径第一次经过对象定义语句时初始化,并且知道程序终止才被销毁,在此期间即使对象所在的函数结束执行也不会对它有影响。

//统计自己被调用了多少次的函数
size_t count_calls()
{
    static size_t ctr = 0;    //调用结束后,这个值依然有效
    return ++ctr;
}

5. 数组形参(必须保证数组不会越界)

void print(const int*)
void print(const int[])
void print(const int[10])    //这几个是等价的,传入的都是数组第一个元素的指针

void print(int (&arr)[10])    //arr是具有十个整数的整形数组的引用,括号不能少

如果我们传给print函数的是一个数组,那么实参自动地转换成指向数组某元素的指针。

void func(int *matrix[10])    //形参是十个指针的数组
void func(int (*matrix)[10])    //形参是指向有十个整数的数组的指针
void func(int matrix[][10])    //形参是指向含有十个整数的数组的指针

6. 处理可变形参

为了编写能处理不同数量实参的函数,C++11新标准提供了两种主要的方法:

        a)所有实参的类型相同,传递一个initializer_list的标准库类型(是一个模板类型)

                initializer_list<T> lst; 默认初始化:T类型元素的空列表
                initializer_list<T> lst{a, b, c,…}; lst的元素和初始值一样多,lst的元素是对应初始值的副本,列表中的元素是const的
                lst2(lst); //拷贝或赋值一个initializer_list 对象不会拷贝列表中的元素,而是共享元素
                lst2 = lst;
                lst.size(); 返回列表中的元素格式
                lst.begin(); 返回指向lst中首元素的指针,不是迭代器
                lst.end(); //返回指向lst中尾元素的下一个元素的指针

使用实例

int sum(initializer_list<int> li)
{
    int total = 0;
    for(auto beg = li.begin(); beg!=li.end(); ++beg)
        total += *beg;

    return total;
}


int main()
{
    int a = 1;
    int b = 2;
    int c = 3;

    cout<<sum({a,b})<<endl;
    cout<<sum({a,b,c})<<endl;    //注意这个格式,其实就是容器的列表初始化
    return 0;
}

特点是:initializer_list对象中的元素永远是常量值,无法改变元素的值。

        b)实参类型不同,之后再介绍

7.省略符形参

省略符形参应该仅仅适用于C和C++通用的类型,大多数类型的对象在传递给省略符形参时都无法正确拷贝。

//省略符形参只能出现在形参列表的最后一个位置,形式只有以下两种
void func (parm_list, ...);
void func (...)

三、返回类型

1. 不要返回局部对象的引用或指针

2. 调用一个返回引用的函数得到左值

3. 列表初始化返回值

vector<int> process()
{
 return {1,2,3,4};
}

4. main函数不能调用它自己

5. 返回数组指针

因为数组不能被拷贝,所以函数不能返回数组。不过可以返回数组的指针或引用。

可以使用简化的类型别名方式。

typedef int arrT[10];
using arrT = int[10];    //两个等价声明,arrT是一个类型别名,表示的类型是含有10个整数的数组

arrT* func(int i);

6. 尾置返回类型

函数的返回值类型放在函数的参数列表后面

形式为在形参列表后面并以一个->符号开始

特点:        

        a) 任何函数的定义都能够使用尾置返回

        b) 对于函数的返回类型比较复杂的更为有效

        c) 使代码更加直观和简洁

        d) 使用“尾置”返回类型来配合auto、decltype使用提高泛型编程

(具体查资料,例子没有看懂)

四、函数重载

1. main函数不能重载

2. 顶层const不影响传入函数的对象,一个拥有顶层const的形参和另一个没有顶层const的形参无法区分开来

3. 如果形参是某种类型的指针或引用,则通过区分其指向的是常量对象还是非常量对象可以实现函数重载,此时的const是底层的。

4. 在C++中,名字查找发生在类型检查之前

5. const_cast(只能转换指针或引用)

使用const_cast去掉const属性,其实并不是真的改变原类类型(或基本类型)的const属性,它只是又提供了一个接口(指针或引用),使你可以通过这个接口来改变类型的值。

const_cast<type_id> (expression)
//typeid和expression的类型是一样的

        作用:

                a)常量指针被转化成非常量的指针,并且仍然指向原来的对象;

                b)常量引用被转换成非常量的引用,并且仍然指向原来的对象;

                c)const_cast一般用于修改底指针。如const char *p形式。

6. 函数的匹配

寻找最佳匹配,即形参类型与实参类型最匹配的那个可行函数。

7.实参类型转换

为了确定最佳匹配,编译器将实参类型到形参类型的转换划分成几个等级

        a)精确匹配:实参类型和形参类型相同;实参从数组类型或函数类型转换成对应的指针类型;向实参添加顶层const或者从实参中删除顶层const

        b)通过const转换实现的匹配

        c)通过类型提升实现的匹配

        d)通过算数类型转换实现的匹配

        e)通过类类型转换实现的匹配(具体见书第219页)

五、特殊用途语言特性

1. 默认实参:在给定作用域内,一个形参只能被赋予一个默认实参。

C++规定,要把没有默认实参的形参放到前面,而默认实参的形参写到靠右的位置,并且一个形参如果有默认实参,那么其右侧必须都有默认实参。即默认实参在尾部。

2. 内联函数

可以避免函数调用的开销,通常内联函数就是在每个调用点“内联”地展开。

一般用于:优化规模较小、流程直接、频繁调用的函数。

很多编译器都不支持内联递归函数。

3. constexpr函数

指能用于常量表达式的函数。

要遵循的约定:函数的返回类型及所有形参的类型都得是字面值类型; 并且函数体中有且只有一个return语句。

constexpr函数不一定返回的是常量表达式。

3. 把内联函数和constexpr函数放在头文件内(通常)

4. 调试帮助

程序可以包含一些用于调试的代码,但是这种代码只在开发程序时使用,当程序编写完成准备发布时,要屏蔽相关调试代码。

使用两种预处理功能:

a)assert

是一种预处理宏(即预处理变量),类似内联函数

assert(expr);

首先对expr求值,如果表达式为假,assert输出信息并终止程序的执行。如果表达式为真,assert什么也不做。

b) NDEBUG预处理变量

assert的行为依赖于一个名为NDEBUG的预处理变量的状态,如果定义了NDEBUG,则assert可以什么也不做。默认状态下是没有定义NDEBUG的。

#ifdef NDEBUG
    #undef NDEBUG
    #include <assert.h>
    #define NDEBUG
#else
    #include <assert.h>
#endif // #ifdef NDEBUG

六、函数指针

1. 函数指针指向的是函数而非对象。

bool (*pf)(const string &, const string &)
//pf指向一个函数,该函数的参数是两个const string的引用,返回值是bool类型

bool *pf(const string &, const string &)
//声明一个名为pf的函数,该函数返回bool*

注意加不加括号的区别,如果是函数指针,则必须要加括号。

2. 使用函数指针

当我们把一个函数名作为一个值使用的时候,该函数自动地转换为指针。

pf = lengthCompare    //pf指向名为lengthCompare的函数
pf = &lengthCompare    //两者是等价的赋值语句:取地址符是可选的

调用函数指针(可以直接使用指向函数的指针调用该函数,无需提前解引用指针)

bool b1 = pf("hello", "goodbye");    //调用length函数
bool b2 = (*pf)("hello", "goodbye");    //等价的调用
bool b2 = lengthCompare("hello", "goodbye");  //另一个等价的调用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值