zoukankan      html  css  js  c++  java
  • C++Primer学习——函数

    编译器能以任意顺序对形参进行求值
    函数的返回类型不能是数组类型和函数类型。
    函数开始时为形参分配内存,一旦函数结束,形参也就被销毁了。

    如果弄成静态局部变量,那么回到程序终止结束时才被销毁。

    void fo()
    {
      static int a ;   //只在第一次初始化
      a++;             //保存了前次被调用后留下的值
      return ;
    }
    //所有的全局变量都是静态变量,而局部变量只有定义时加上类型修饰符static,才为局部静态变量
    

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

    f(int a,int b) 被调用时可以看成  f(int a = x,int b = y);
    f(int &a,int &b) 被调用时可以看成  f(int &a = x,int &b = y);
    

    使用引用避免拷贝:
    拷贝大的类类型或者容器的对象比较低效,甚至有的类类型不支持拷贝操作

    参数的const:
    顶层const会被忽略,所以两个fcn的参数是一样的(顶层const没法区分参数)

    void fcn(const int a){}
    void fcn(int a){}
    

    尽可能使用常量引用:

    void find(string &s){
    }
    void find_char(const string &s){
    }
    find("hhh");                  //error 普通引用不能引用字面值
    find_char("hhh");             //ok
    

    但底层const看成不同的参数(作用于不同的对象 常量or非常量)
    编译器可以通过实参是否是常量来判断调用哪一个(而且非常量优先选择非常量版本)

    Record look(Account *)
    Record look(const Account *)
    Record look(Account &)
    Record look(const Account &)
    

    含可变参数:

    Initializer_list:
    initializer_list是C++标准程序库中的一个头文件,定义了C++标准中一个非常轻量级的表示初始化器列表的类模板initializer_list及有关函数。
    与vector不同的是,initializer_list对象中的元素永远是常量值,我们无法改变initializer_list对象中元素的值。
    initializer_list lis(ls);
    initializer_list lis = ls;   //原始列表和副本共享元素
    

    C++11允许构造函数和其他函数把初始化列表当做参数

    void fcn(initializer_list<int> ls)
    {
        for(auto i : ls)
        {
            cout << i <<" ";
        }
        cout << endl;
    }
    int main()
    {
        fcn({1,2,3});
        return 0;
    }
    class node
    {
        public:
        node(int a,int b){}
        node(initializer_list<int>) {}
        node(){};
    };
    int main()
    {
        node a;
        node b(1,2);
        node c{2,3,4,5,6};
        node d = {1,3,4,5,6};
        return 0;
    }
    

    省略符类型:

    只是用与C/C++通用类型,大多数类类型在传递省略符形参时都无法正确拷贝

    void foo(int a,...)
    void foo(...)
    

    返回值:

    返回值的方式和初始化类似,返回值会被拷贝到函数的调用点用于初始化,如果返回引用又是另一回事。
    而且别返回局部变量指针或者引用,因为函数结束一会后其所占空间会被释放掉

    int& fo(int a)
    {
        int b = a;
        cout << &b <<endl;
        return b;
    }
    int main()
    {
       int &a = fo(100);         //a,b地址相同,说明并没有经过拷贝
       cout << &a <<endl;
       cout << a <<endl;
    }
    

    函数的返回类型决定返回是否为左值:

    当函数返回引用的时候所得到的是左值,其它所得到的都是右值。

    char &to_Get(string& a,int index)
    {
        return a[index];
    }
    int main()
    {
       string ta("abcd");
       cout << ta <<endl;
       to_Get(ta,1) = 'B';
       cout << ta << endl;
       /*
       abcd
       aBcd
       */
    }
    

    返回数组指针:

    数组因为不能被拷贝,所以函数不能返回数组。可以返回数组的引用和指针,只是比较麻烦.
    ①.利用别名简化。

    typedef int arr[10];
    using arr = int[10];
    arr* func(int i);
    

    ②.声明一个返回指针的函数(从内往外读)

    int (*a)[10];
    int (*func(int a,))[10];                 //格式相似
    

    ③.尾置返回类型
    在->符号后面指明函数的真正返回类型

    auto func(int a) -> int(*)[10]
    {
    }
    

    ④.使用decltype(但是会返回数组类型,而数组又无法赋值,所以搞成指针)

    int odd[] = {1,3,5,7};
    decltype(odd)* func()
    {
        return &odd;          //返回数组类型的指
    }
    int main()
    {
       auto p = func();
       cout << p << " " <<odd <<endl;
    }
    

    重载:

    函数重载:应该在函数参数个数或者形参类型上面有所不同.
    如果在当前作用域中找到了所需的函数名,那么编译器会自动忽略外层作用域的同名实体。(不同作用域不能重载函数名)

    void Print(string a);
    void Print(double a);
    void f(int a)
    {
    	void Print(int);
    	print("abc");     //error:与int不符
    	Print(2.5);       //输出了int
    }
    void Print(double a){ cout << "double " << endl;} 
    void Print(int a){cout << "int" << endl;}
    void Print(string a){cout << "string" << endl;}
    

    默认实参:

    一旦某个形参被赋予了默认实参,那后面全部都要有默认值。 而且函数调用时,只能省略尾部实参

    windows = screen(,,'s');      //error
    windows = screen('s');
    

    函数可以被声明多次,但是一个形参只能被赋予一个默认值。

    string screen(sz,sz,char = ' ');
    string screen(sz,sz,char = '?');             //error,重复声明
    string screen(sz=24,sz=80,char);             //correct,添加默认实参
    

    (局部变量不能作为默认实参)

    int pa = 100;
    void fo(int a = pa);              //默然实参为pa
    void f()
    {
        int pa = 80;                  //隐藏了外部的pa
        fo();                         //没改变默认值
    }
    int main()
    {
       f();
    }
    void fo(int a)
    {
        cout << a <<endl;
    }
    

    内联函数:

    调用函数比一般的求表达式的值要慢一点,大多数机器上,一次函数调用包含着很多工作:调用前先保存寄存器,并在返回时恢复;
    可能需要拷贝实参;程序转下一个新的位置继续执行。
    内联函数(优化规模小,流程直接,调用频繁的函数; 有的编译器不支持):

    cout<<shorterString(s1,s2)<<endl;
    //在编译器看来会转换成
    cout << (s1.size() <= s2.size() ? s1:s2) <<endl;
    

    constexpr函数:

    编译器把对constexpr函数的调用替换成它的结果了,被隐式地指定为内联函数。
    允许返回值并非一个常量?
    A (non-template) constexpr function must have at least one execution path that returns a constant expression
    //必需要能返回至少一个常量表达式

    constexpr int f(bool b) { return b ? throw 0 : 0; }     // OK
    constexpr int f() { return f(true); }     // ill-formed, no diagnostic required
    template<bool B> constexpr int g() { return f(B); }    // OK
    constexpr int h() { return g<true>(); }    // ill-formed, no diagnostic required
    http://stackoverflow.com/questions/31206937/a-constexpr-function-is-not-required-to-return-a-constant-expression
    

    但如果把函数用在需要常量表达式的上下问时,则编译器会检测函数结果是否符合要求
    内联函数和constexpr函数可以在程序中被定义多少,通常被定义在头文件

    assert:
    一种预处理宏,行为类似内联函数。 assert(expr)//cassert头文件,由预处理器管理,不需要命名空间
    assert(word.size() > threshold);
    如果定了NDEBUG预处理变量,那么assert什么也不做

    参数匹配:

    1.与被调用函数同名 2.其声明在调用点可见
    3.形参数量相同 4.实参和形参的类型相同,或者能够转换
    然后从可行的当中找出最佳匹配
    匹配成功:①每个实参的匹配都不劣于其它可行函数的匹配
    ②至少有一个实参匹配优于其它
    否则会产生二义性

    void f(int a,int b)
    void f(double a,double b)
    int main()
    {
        f(3,2.15);
    }
    

    上面这个例子,两个函数中找不到一个脱颖而出的。 所以会产生二义性。
    //要避免强制类型转化,出现只能说明我们的形参集合设置的不合理

    匹配等级:
    ①精确匹配:
    1.实参和形参的类型相同
    2.从数组类型或者函数类型转换它们的对应的指针类型
    3.想实参中添加顶层const或者删除顶层const
    //2016年11月20日 22:00:24
    ②通过const转换 (添加底层const:const&什么的)
    ③通过类型提升 (小类型提升为大类型)
    ④通过算术类型转换或者指针转换 (运算符的运算对象转换成最宽;0或NULL转换成任意指针,任意非常量对象指针->void,任意对象->const void
    ⑤类类型转换

    void f(float x);
    void f(int x);
    f(3.45);   //double型,而所有算术转换等级相同,产生二义性
    void f(int a);
    void f(short b);
    f('a');          //类型提升,但是会提升为较大范围的int。只有类型是short时才会调用short版本
    

    函数指针:

    函数的类型是由它的返回类型和行参类型决定的。 函数可以自动地转换成指针,指针可以调用该函数

    bool lengthCompare(const string&,const string &);
    bool (*p)(const string&,const string&);
    p = lengthCompare;
    p = &lengthCompare;    //等效,函数名会转换成指针
    bool t = p("hello","bye");
    bool t = (*p)("hello","bye");      //等效
    

    对于重载函数,编译器会通过指针类型选哪个函数,指针类型必需与重载函数的某一个精确匹配.
    函数指针可以作为一个函数的形参。
    简化函数指针代码 decltype and typedef:

    typedef bool func(const string&,const string&);         //函数
    typedef bool (*func)(const string&,const string&);      //函数指针
    typedef decltype(lengthCompare) func2;
    typedef decltype(lengthCompare) *func2;
    decltype()会返回一个函数类型而且不会自动转换成指针
    

    返回函数的指针:

    using p = int(int*,int );
    using tp = int(*)(int*,int );
    
    tp f1(int);              //correct   tp是指向函数的指针
    p f1(int);               //error  p是函数类型,不能返回一个函数
    p* f1(int);              //correct
    int (*f1(int*,int))(int*,int );     //直接声明一个返回函数指针的函数
    

    首先f1前面*,所以f1返回一个指针;指针类型包含形参列表,所以指针指向一个函数

    尾置型:

    auto f1(int) -> int (*)(int*,int);
    
  • 相关阅读:
    List sort()方法
    解析器
    beautifulsoup库
    break 语句
    enumerate函数
    POJ 1915 Knight Moves
    POJ 1745 Divisibility
    POJ 1731 Orders
    POJ 1664 放苹果
    POJ 1606 Jugs
  • 原文地址:https://www.cnblogs.com/Przz/p/6414337.html
Copyright © 2011-2022 走看看