C++primer-learning-notes-chapter6

242 阅读5分钟

参数传递问题

  • 形参初始化的机理和变量初始化一样。
  • 引用传递(passed by reference):又称传引用调用(called by reference),指形参是引用类型,引用形参是它对应的实参的别名。
  • 值传递(passed by value):又称传值调用(called by value),指实参的值是通过拷贝传递给形参。
  • 当初始化一个非引用类型的变量时,初始值被拷贝给变量。
  • 函数对形参做的所有操作都不会影响实参。
  • 指针形参:传入的也是指针值的拷贝,函数内部改变其值不影响实参的值。常用在C中,C++建议使用引用类型的形参代替指针。

传引用参数

  • 通过使用引用形参,允许函数改变一个或多个实参的值。引用形参绑定初始化它的对象,而非对象的副本。
  • 经常用引用形参来避免不必要的复制。
  • 如果无需改变引用形参的值,最好将其声明为常量引用。

const形参和实参

  • 形参的顶层const被忽略。就是说
void fcn(const int i){} //能读取i,但不能向i写值
void fcn(int i){} //这个属于重复定义
  • 当形参有顶层const时,传给它常量对象或者非常量对象都是可以的。
  • string& s 形参无法接收如"hello world"的实参,需要定义形参为const string& s

数组形参

  • 不允许拷贝数组,使用数组时通常会将其转换成指针。
//等价
void print(const int*);
void print(const int[]);
void print(const int[10]);
  • 要注意数组的实际长度,不能越界。
//使用标记指定数组长度 处理C风格字符串遇到空字符停止:
void print(const char* cp){
    if(cp)
        while(*cp)
            cout<<*cp++;
}

//使用标准库规范
void print(const int *beg,const int *end){
    while(beg!=end)
        cout<<*beg++<<endl;
}
int j[2] = {0,1};
print(begin(j),end(j));

//显式传递表数组大小的形参
void print(const int ia[],size_t size){
    for(size_t i = 0;i != size;++i){
        cout<<ia[i]<<endl;
    }
}
int j[] = {0,1};
print(j,end(j)-begin(j));

数组引用形参

void print(int (&arr)[10]){  //arr是具有10个整数的整型数组的引用
    for(auto elem : arr)
        cout<<elem<<endl;
}

传递多维数组

void print(int (*matrix)[10],int rowSize){}
void print(int matrix[][10],int rowSize){} //等价定义

可变形参 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中微元素下一位置的指针
  • 是一种模板类型,对象中的元素永远是常量值,类型相同
  • 类型不同时,可用可变参数模板
void error_msg(initializer_list<string> il){
    for(auto beg = il.begin();beg!=il.end();++beg)
        cout<<*beg<<" ";
    cout<<endl;
}
error_msg({"functialX",expected,actual}) //expected,actual是string对象

省略符形参

 void foo(parm_list,...);
 void foo(...);

函数返回值

  • 值的返回:返回的值用于初始化调用点的一个临时量,该临时量就是函数调用的结果。非引用类型也存在拷贝。
  • 不要返回局部对象的引用或指针。
  • 引用返回左值:函数的返回类型决定函数调用是否是左值。调用一个返回引用的函数得到左值;其他返回类型得到右值。
  • 列表初始化返回值:函数可以返回花括号包围的值的列表。
vector<string> process(){
    ...
    return {};
    //return {"fff",string1,string2};
}

返回数组指针或引用

  • 使用类型别名简化
typedef int arrT[10];
using int = arrT[10];
arrT* func(int i); //func返回一个指向含有10个整数的数组的指针
  • 声明一个返回数组指针的函数
int (*func(int i))[10];
  • 使用尾置返回类型
auto func(int i) -> int(*)[10];
  • 使用decltype
int odd[] = {};
int even[] = {};
decltype(odd) *arrPtr(int i){
    return (i%2) ? &odd : &even;
}

函数重载

  • 一个有顶层const的形参和没有它的函数无法区分。
Record lookup(Phone);
Record lookup(const Phone);

Record lookup(Phone*);
Record lookup(Phone* const);
  • 相反,是否有某个底层const形参可以区分。
Record lookup(Account&);
Record lookup(const Account&);

Record lookup(Account*);
Record lookup(const Account*);

const_cast和重载

  • const_cast在重载函数的情景中最有用
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);
}

内联函数

  • 普通函数的缺点:调用函数比求解等价表达式要慢得多。
  • inline函数可以避免函数调用的开销,可以让编译器在编译时内联地展开该函数。

constexpr函数

  • 指能用于常量表达式的函数。
  • constexpr int new_sz() {return 42;}
  • 函数的返回类型及所有形参类型都要是字面值类型。

函数指针

  • 是指向函数的指针。
  • 声明一个可以指向该函数的指针
bool lengthCompare(const string&,const string&);
bool (*pf)(const string&,const string&);
  • 当我们把函数名作为一个值使用时,该函数自动地转换成指针。
pf = lengthCompare;
pf = &lengthCompare;

bool b1 = pf("hello","goodbye"); //调用该函数
bool b2 = (*pf)("hello","goodbye");
bool b3 = lengthCompare("hello","goodbye");

重载函数的指针

void ff(int*);
void ff(usigned int);

void (*pf1)(unsigned int) = ff; 

函数指针形参

void useBigger(const string& s1,const string& s2,bool pf(const string&,const string&));
void useBigger(const string& s1,const string& s2,bool (*pf)(const string&,const string&));

useBigger(s1,s2,lengthCompare);

//简化
typedef bool Func(const string&,const string&);
typedef decltype(lengthCompare) Func2;

typedef bool(*FuncP)(const string&,const string&);
typedef decltype(lengthCompare) *FuncP2;

void useBigger(const string&,const string&,Func); //Func自动转为指针
void useBigger(const string&,const string&,FuncP2);

返回指向函数的指针

using F = int(int*,int);
using PF = int(*)(int*,int);

PF f1(int);
F *f1(int);

//直接声明
int (*f1(int))(int*,int);

//尾置返回类型
auto f1(int) -> int(*)(int*,int);

//将auto和decltype用于函数指针类型
string::size_type sumLength(const string&,const string&);
string::size_type largerLength(const string&,const string&);
decltype(sumLength) *getFcn(const string&);