《c++ primer》第六章 函数

#include "stdafx.h"
#include<iostream>
#include"Chapter.h"
using namespace std;
void swap(int*d1, int* d2) {//形参类型是指针
    int temp;
    temp = *d1;
    *d1 = *d2;
    *d2 = temp;
}
void swap(int& d1, int& d2) {//形参类型是引用
    int temp;
    temp = d1;
    d1 = d2;
    d2 = temp;
}
unsigned absolut(int data) {//取绝对值函数
    return abs(data);
}
int main()
{
    int n,result;
    cout << "input two intgret number:" << endl;
    cin >> n >>result;
    cout << "before switch" << " n=" << n << endl;
    cout << "result=" << result << endl;
    //swap(&n, &result);//形参为指针类型
    swap(n, result);//形参为引用类型
    cout << "n=" << n << endl;
    cout << "result=" << result << endl;
    system("pause");
    return 0;
}

6.2.2传引用参数

作用:
(1)可以作用于实参,改变实参的值
(2)避免拷贝大的数据类型,低效,有些数据类型不支持拷贝,比如I/O
(3)函数返回引用可以返回多个值

 string::size_type find_char(const string& s, char c, string::size_type& occurs) {//变量串
    auto posi = s.size();
    for (decltype(posi) i = 0;i != s.size();i++) {
        if (s[i] == c) {
            if (posi == s.size())
                posi = i;
            occurs++;
        }
    }
    return posi;
}
//判断是否是一个句子
bool is_sentence(const string& s) {
    string::size_type n=0;
    return find_char(s, '.', n) == s.size() - 1&&n==1;//句号在串末尾,且只有一个句号
}
int main()
{
    string s = "huangbinhase";//常量串
    string::size_type n = 0,position = 0;
    position = find_char(s, 'u', n);
    cout << "the rank of fist charter 'u' is:" << position << endl;
    cout << "the number of 'u' is:" << n << endl;
    cout << "the s is a sentence Y/N?" << endl;
    if (is_sentence(s))cout << 'Y' << endl;
    else
        cout << 'N' << endl;
    system("pause");
    return 0;
}

注意:在visual studio 15中,常量串传给变量串的引用竟然不报错:

6.2.3 const形参和实参

(关于顶层和底层const的补充)
底层:是指最终指向的值(*p=4,顶层是4,底层为p,大小为存放4的内存单元的地址)
顶层:指针本身为常量(一般为地址.)
1、尽量使用常引用
(1)说明了常引用不能改变实参的值。
(2)可以扩大实参的接受范围,如果为普通引用就不能接受实参是const类型的数据

习题判断是否有大写字母:

#include<string>
#include<cctype>
using namespace std;
int main()
{
    string s = "huanginhase";
    cout << "is there upper charter in string s ,Y/N?" << endl;
    for (auto &i : s) {
        if (isupper(i)) {//如果发现了大写字母,立即跳出循环,输出Y
            cout << "Y" << endl;
            break;
        }
        if (i == s.at(s.size() - 1))//如果i已经扫描到了s最后一个元素,则说明没大写字母
            cout << 'N' << endl;
    }
    system("pause");
    return 0;
}

6.2.4数组形参

1、两个性质:

(1)不允许拷贝数组
(2)使用数组是会转化为指针

void print(const int*)
void print(const int[])
void print(const int [10])

这三个函数是等价的(函数并不知道数组有几个元素,通常穿数组时,还有穿数组元素个数)

2、三种方法管理指针类型的形参,防止越界

(1)c风格字符串’\n’结尾,只适用于有明显标记的数组,具有局限性
(2)使用标准库bigin(),和end()函数。例如: print(begin(数组名), end(数组名));
(3)显示传递数组大小的形成(end(i)-begin(j))

3、数组的引用的形参(很重要)

1,声明形式

void print(int (&arr)[10]);

注意此时print函数知道传递的数组大小为10个元素,如果实参元素个数不为10,则报错。

6.2.5 main: 处理命令行选项

agv第一个字符串是程序名,从第二个开始才是真正接受的参数

//编写main函数,接受两个参数,把实参的内容连成一个string输出
#include "stdafx.h"
#include<iostream>
#include<cstring>//c风格字符串函数头文件
//strlen(s),strcmp(s1,s2),strcat(s1,s2)
using namespace std;
int main(int argc, char** argv)
//第二个形参为c风格字符串,第一个参数代表字符串个数
{
    strcat_s(argv[1],200, argv[2]);//第二个参数缓冲区大小
    puts(argv[1]);
    cout << endl;
    system("pause");
    return 0;
}

调用cmd,运行结果:


在上述命令中,首先设置运行路径。

6.2.6 含有可变形参的函数

动机:有时候我们无法提前预知向函数传递几个实参
处理不同实参数量的函数,C++提供了两方法:
(1)initializer_list 模板类型,此时所有的实参类型相同
(2)可变参数模板,可实现实参类型不同的情况(后面再讨论)

1、 initializer_list形参

initializer_list有点像vector类,但是inilializer_list对象元素永远是常量。

#include "stdafx.h"
#include<iostream>
#include<string>
//#include<inilializer_list>//无法打开,该编译器不支持该类
#include<vector>
using namespace std;

//输出错误信息函数
void error_msg(vector<string> alam) {//由于vs15中inilializer_list类文件不支持,故用vector类来模拟
    for (auto beg = alam.begin();beg != alam.end();++beg)
        cout << *beg << ' ';//单词之间用空格符隔开
    cout << endl;
}
int main()
{
    string expect="huangbin", actual="huangbin";
    if (expect != actual)//不相等,则两者都输出
        error_msg({ "functonX", expect, actual });//三个参数
    else      //相等则表示正常
        error_msg({ "functionX", "okay" });//调入两个参数
    system("pause");
    return 0;
}

输出结果:
这里写图片描述

##### 省略符形参

void foo(parm_list,...);
void foo(...); //省略符号放到最后

6.3 返回类型和return语句

return语句有两种形式:

//无返回值
return;//只能用在返回值为void的函数中,可以用在提前退出,
//有返回值类型
return expression;

6.3.2 有返回值函数

返回值类型必须与函数返回值类型相等,或者能隐形的转换

#include "stdafx.h"
#include<iostream>
#include<string>
using namespace std;

//判断两字符中,一个是另一个的前缀
bool str_subrange(const string& str1, const string& str2) {
    if (str1 == str2) return true;
    auto size = (str1.size() < str2.size()) ? str1.size() : str2.size();//以短字符串长为限
    for (decltype(size) i = 0;i != size;++i)
        if (str1[i] != str2[i])return false;
    return true;//注意循环后必须含有此return语句,不然未定义
}
int main()
{
    string s1 = "haing", s2 = "shiena";
    if (str_subrange(s1, s2))cout << "OK" << endl;
    if (!str_subrange(s1, s2))cout << "NO" << endl;
    system("pause");
    return 0;
}
值是如何被返回的
string make_plural(size_t ctr,const string &word,const string& ending){
         return (ctr>1)?word+ending:word;
}

拷贝word或者两种之和,复制给调用点临时变量
如果返回时引用类型,则是返回值得别名,所以引用类型或者指针的返回类型千万不能是局部对象。因为函数执行完后,该变量都被销毁,成为也指针。

const string &manip() {
    string ret;
    if (!ret.empty())
        return ret;//严重错误,返回局部对象引用
    else
        return "Empty";//发生严重错误,"empty"是个临时局部变量
}

该函数运行会发生异常,编译不会报错!

返回类类型的函数和调用运算符
//函数的返回类型为指针、引用、类的对象,可以用调用的结果来访问结果对象的成员
auto sz=shorterString(s1,s2).size();//满足左结合率
引用返回左值

调用一个返回类型为引用的函数得到左值,可以给它复制:

char& get_val(string& s1);
get_val(s)='s';//合法,但不实用
列表初始化返回值

C++11新标准规定,函数可以返回花括号包围的列表,该列表也用来对返回的临时变量进行初始化。

string expect, actual;
vector<string> process() {
    if (expect.empty())
        return{};
    else if (expect == actual)
        return{ "functionX","ok" };//列表初始化vector<string>对象
    else
        return{ "functionX",expect,actual };
}

例子:

vector<string> si=process();//函数的返回值初始化si

如果返回类型是类类型,则由类本身定义初始值如何使用。

主函数main的返回值

如果函数的返回值不为空,则必须返回一个值。但是main函数是个例外,允许main函数没有return语句,因而在函数末尾编译器隐式插入一条return 0语句。

6.3.3 返回数组指针

定义返回数组指针或引用类型的函数,方法一;
(1)类型别名

typedef int arrT[10];//arrT是一个类型别名,它表示的类型含                            有10个元素
using arrT=int[10];//与上等价
arrT* func(int i);//返回一个指向含有10个整数的数组的指针
声明一个返回数组的指针的函数
int arr[10];//arr是一个含有10个元素的数组
int *p[10];//p是一个含有10个指针的数组
int (*p)[10];//p是一个指向含有10个元素的指针

第2种方法,不用类型别名:

type (*function(parameter_list))[dimension]//一般格式
int (*func(int i))[10];//例子,与上面一样效果

第3种方法;使用位置返回类型(C++11)

auto func(int i)->int(*)[10];//把返回类型放到了形参列表之后,可以清楚的看出返回的类型

第4种方法:decltype()

int odd[10];
decltype(odd) *arrPtr(int i);//注意decltype(odd)的结果是个数组,不负责转换成指针,所以还需家个*

同理引用类型定义:

typedef string (&str10)[10];
//using s10=string (&)[10];
string huang[9] = {"hang","hianeie",""};
str10 b = huang;//类型不匹配,编译报错,一个是9个元素一个是10个元素,但是当huang[10],就好了

与数组进行对比来看

typedef int str10[10];
//using str10 = int [10];
int huang[10] = {10,2,3,3,3,2,21,4,3,5};
str10 b;
b = huang;//编译通不过,因为对于编译器来说huang的个数不确定的,只是一个指针,但是b的类型一定是10个元素的,类型不匹配


//完全编译通过,运行正确
typedef int (&str10)[10];
int huang[10] = { 9,8,9,3,4,4,3,2,1,23};
str10 b = huang;

6.4 函数的重载

重载和const形参
void faction(int,int);
void faction(const int,const int);//函数重复定义了,因为这两个函数对实参没有什么要求,常量和非常量都可以


void faction(int&,int&);
void faction(const int&, const int&);//两个是不一样的函数,第一个如果实参是常量就不接收,非常量的话,首先调用第一个函数,虽然第二个函数也可以用。

6.4.1 重载与作用域

调用函数首先总是在同一区域内找是否有该函数声明,如果找到了,就会忽略外层的同名函数,尽管内层函数参数不匹配,外层匹配。也不会去调用外层的同名函数。

6.5特殊用途的语言特性

6.5.1 默认实参
typedef string::size_type sz;
string screen(sz ht=24,sz wid=80,char backgrand=' '};

如果调用者不给的实参,则该函数,就用默认值。
不过注意:一旦某个形参被赋予了默认值,它后面的形成必须有默认值
所以要想最后个实参传值,前面的形成必须也给传值。
所以设计默认实参时,最好把那些不怎么改动的形参放在形参列表后面,经常需要个性化的形参放在前面。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值