Zheng Chu's Blog

让希望永驻


  • 主页

  • 所有专栏

  • 历史文章

  • 标签

  • 关于我

C++---CHAPTER-6---FUNCTION

Posted on 2019-09-04 Edited on 2020-12-06 In C++ Views:

参数传递

  1. 传值调用
  • 指针形参:
    1
    2
    3
    4
    5
    void reset(int *p)
    {
    *ip = 0; // 改变指针ip所指对象的值
    ip = 0; // 只改变了ip的局部拷贝,实参未被改变
    {
    2.传引用调用
  • 如果函数无需改变引用形参的值,最好将其声明为常量引用。
  • const形参和实参,当使用实参初始化形参,会忽略掉顶层const:也就是说,当形参有顶层const,传给它常量对象或者非常量对象都是可以的:
    1
    void fn(const int i) {/* fn能读取i,但是不能向i写入值*/}
  • 尽量使用常量引用如 const i&,而不是普通引用i &,因为我们不能把const对象、字面值、或者需要类型转换的对象传递给普通的引用:
    1
    2
    3
    4
    5
    string is_sentence(const string &s)
    {
    string::size_type ctr = 0;
    return find_char(s, '.',ctr) == s.size() - 1 && ctr == 1;
    }
    其中如果find_char的第一个形参是普通引用string&,程序会失败,因为s是常量引用。

返回类型和return语句

  • 引用返回左值:调用一个返回引用的函数返回左值,其他返回类型得到右值,和其他左值一样它也能出现在赋值运算符的左边:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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'; // 将s[0]的值改为A
    cout<< s << endl;
    }

    如果返回类型是常量引用,则不能给结果赋值:

    1
    shorterString("hi", "bye") = "X"; //错误,返回类型是一个常量
  • 列表初始化返回值:函数可以返回花括号{}包围的值:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    vector<string> process()
    {
    if (expected.empty())
    return {}; // 返回一个空的vector对象
    else if (expected == actual)
    return {"functionX", "okey"); //返回列表初始化的vector
    else
    return {"functionX", expected, actual};
    }
  • 返回数组指针
    定义一个返回数组指针的函数,则数组的维度必须跟在函数名字之后。

1
2
3
4
int arr[10];
int (*p2)[10] = &arr; //p2是一个指针,指向含有10个整数的数组

int (* func(int i))[10]; //解引用func的调用得到一个大小为10的数组
  • 使用尾置返回类型,进一步简化返回类型复杂的函数
    1
    auto func(int i)->int(*)[10];  //func返回的类型是一个指针,该指针指向含有10个整数的数组。
  • 使用decltype的情况,我们知道函数返回的指针指向哪个数组:
    1
    2
    3
    4
    5
    6
    7
    int odd[] = {1,2,3,4,5};
    int even[] = {3,4,52,,5,1};
    // 已知返回一个指针指向5个整数的数组
    decltype(odd) *arrPtr(int i)
    {
    return (i % 2) ? &odd : &even; //返回一个指向数组的指针
    }
    注:decltype表示他的返回类型是个指针,并且该指针所指的对象与odd的类型一致。但是decltype不会把数组类型转换成对应的指针,所以decltype的结果是一个数组,要表示arrPtr是一个指针,必须在函数声明的时候加上一个*。
  • 重载和const形参
    顶层const不影响传入函数的对象:
    1
    2
    Record  lookup(Phone);
    Record lookup(const Phone); //重复声明了上面的函数
    当形参是某种类型的指针或引用,则区分常量对象和非常量对象可以实现函数重载:
    1
    2
    Record lookup(Account&);  //作用于Account的引用
    Record lookup(const Account&); // 新函数,作用于常量引用
  • const_cast和重载 :重载函数时,对实参做强制转换成对const的引用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    #include <iostream>
    #include <string>
    using namespace std;

    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);
    }
    int main() {

    string s1{"pig"};
    string s2{"dunk"};
    string &ans1 = shorterString(s1,s1);
    cout <<" ans1 is " << ans1 << endl;
    const string s3{"moster"};
    const string s4{"tigger"};
    const string &ans2 = shorterString(s3,s4);
    cout <<" ans2 is " << ans2 << endl;
    }
  • constexpr函数:是指能用于常量表达式的函数。
    函数的返回类型及所有形参的类型都得是字面值类型;
    函数体中必须有且只有一条return语句。
    为了在编译过程随时展开,constexpr函数被隐式地指定为内联函数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    constexpr  int new_sz() {return 42;}
    constexpr int foo = new_sz(); //正确 foo是一个常量表达式

    constexpr size_t scale(size_t cnt){
    return new_sz() * cnt;
    }
    int arr[scale(2)]; // 正确:scale(2)是常量表达式
    int i = 2; //i不是常量表达式
    int a2[scale(i)]; //错误

注: 对于某个给定的内联函数或者constexpr函数来说,它的多个定义必须完全一致。因此,内联函数和constexpr函数通常定义在头文件中。

  • assert预处理宏,预处理变量,有预处理器而非编译器管理,因此可以直接使用处理名字无须使用using声明。
  • 编译器为每一个函数定义了:
    1
    2
    3
    4
    5
    __FILE__   
    __LINE__
    __TIME__
    __DATE__
    __FUNC__
  • 函数指针:指向的是函数而非对象,函数的类型有它的返回值还有它的形参共同决定,与函数名无关。
1
2
bool lengthCompare(const string&, const string &);
bool (*pf) (const string &, const string &); // 未初始化,pf指向一个函数,该函数的参数是两个const string的引用,返回值是bool类型
  • 使用函数指针:函数名作为一个值使用时,该函数自动地转换成指针:

    1
    2
    3
    4
    5
    6
    pf = lengthCompare;  // pf 指向名为lengthcompare的函数
    pf = &lengthCompare; // 等价上式


    bool b1 = pf("hello", "goodbye"); // 调用lengthcompare函数
    bool b2 = (*pf)("hello", "goodbye"); //等价的调用
  • 函数指针形参:形参定义为指向函数的指针,所以看着像函数类型,实际被当做指针使用:

    1
    2
    3
    4
    // 第三个形参是函数类型,自动转换为指向函数的指针;
    void useBigger(const string &s1, const string &s2, bool pf(const string &, const tring &));
    //等价的声明
    void useBigger(const string &s1, const string &s2, bool (*pf) (const string &, const tring &));

    这就能直接把函数作为实参使用:

    1
    useBigger(s1, s2, lengthCompare);
  • 考虑使用类型别名和decltype简化声明:

    1
    2
    3
    4
    5
    6
    7
    // Func与Func2是函数类型
    typedef bool Func(const string &, const string &);
    typedef bool decltype(lengthCompare) Func2; //等价的类型

    // 下面两个是指向函数的指针
    typedef bool(*FuncP) (const string&, const string&);
    typedef decltype(lengthCompare) *FuncP2; // 等价的类型

    注:

  1. 含有typedef的声明语句定义的不在是变量而是类型别名。
  2. decltype返回函数类型,不会将函数类型自动转换为指针类型,所以结果的前面加上*,得到指针。

使用的时候:

1
2
void useBigger(const string &s1, const string &s2, Func);
void useBigger(const string &s1, const string &s2, FuncP2);

  • 返回指向函数类型的指针:必须把返回类型写成指针形式,编译器不会自动将函数返回的类型当成对应的指针类型处理。

使用类型别名:

1
2
3
using F = int(int* , int); // F是函数类型不是指针
using PF = int(*)(int *, int); // PF是之真理类型
PF f1(int); // PF是指向函数的指针,f1返回指向函数的指针

尾置返回类型的方式声明一个返回函数指针的函数:
1
auto f1(int) -> int(*) (int*, int);

# PRIMER
C++---CHAPTER-3---CONTAINER
C++---CHAPTER-4---EXPRESSION
  • Table of Contents
  • Overview
Zheng Chu

Zheng Chu

90 posts
20 categories
25 tags
GitHub 简书 CSDN E-Mail
  1. 1. 参数传递
  2. 2. 返回类型和return语句
© 2021 Zheng Chu
Powered by Hexo v4.2.1
|
Theme – NexT.Pisces v7.3.0
|