首页 > 代码库 > C++总结之函数重载

C++总结之函数重载

1)函数名称相同

2)函数声明在同一个作用域内(一个类构成一个作用域,因此子类不能重载继承自父类的函数,多继承时,继承自不同父类的函数也不能构成重载)

example:

class Person
{
public:
    void love(){}
};

class Man:public Person
{
private:
    void love(int i){}
};

class Car
{
public:
    void on()
    {

    }
};

class Computer
{
public:
    void on(int arg)
    {

    }
};

class ModernCar:public Car,public Computer
{

};

void love()
{
    Man m;
    m.love();   ///错误,函数名称的查找期间并不区分访问限制
    ///继承自父类的同名函数不能构成重载,已被隐藏尽管子类的love(int)是private的,而父类的love是public的
    

    ModernCar mc;
    mc.on();   ///错误,来自不同空间的函数不能重载,导致歧义
}

3)using声明的引入对函数的重载有影响,其中using namespace相当于把某个命名空间内的名称复制到当前作用域,而using namespace::name 则相当于告知编译器在namespace中查找name。具体影响看例子:

example:

namespace XXX
{
    void fx()
    {
        cout<<"xxx"<<endl;
    }
}

void fx(int a)
{
    cout<<"::"<<endl;
}

void fca()
{
    ///ok
    using namespace XXX;
    fx(0);
    fx();
}

void fcb()
{
    using XXX::fx;
    fx();  ///ok;
    fx(0); 错误,
}

4)函数形参的个数或者类型不同,普通类型的形参不区分const,指针或者引用类型的形参其指涉物的const型别不同也可以构成重载。对于成员函数而言是否为const函数,是针对右值还是左值的函数也可以构成重载。函数的返回值类型不能构成重载

///这两个函数不构成重载,有歧义
int love(int i)
{
    
}

void love(const int i)
{
    
}

///这四个函数构成重载
void love(int *p)
{
    
}

void love(const int *p)
{
    
}

void love(int &arg)
{
    
}

void love(const int &arg)
{
    
}

class Boy
{
public:
    ///这两个函数构成重载
    void love()const
    {
        
    }
    
    void love()
    {
        
    }
};

class Girl
{
public:
    ///这两个函数构成重载
    void love()&
    {
        
    }
    
    void love()&&
    {
        
    }
};

5)函数调用发生时,先从最里层的作用域查找函数名,如果未找到,再到外层查找,直到全局空间,如果任未找到相同的函数名,则查找失败。一旦在某一层找到了相同的函数名,马上停止向外层空间的查找,该层空间所找到的所有相同函数名构成候选函数(对于类作用域来说,父类是子类的外层作用域;对于多继承来说,一个子类会出现多个外层空间,此时,每个外层空间都平等对待,即不存在找到名字后马上停止的情况,而是要查找完它所有的父类,该过程完成后,如果出现不同父类拥有同名函数,则调用出错)第一个例子已经可以说明这个问题了

6)函数名查找成功,则判断可以调用的函数组,实以调用的函数其形参个数和实参必须相同,实参必须可以转化到形参,如果没有可以调用的函数,则调用出错。

7)可调用函数组确定下来以后,根据实参到形参所需做的转化类型判断最佳调用。如果没有最佳调用,则调用出错。

8)形参到实参的转化排序:

①精确匹配,即形参和实参个数类型完全一致

实参到形参的转化是数组名或函数名到其对应指针的转化

针对对象本身的const转化(对于引用来说不存在常引用,因此不存在这种类型的转化,对于指针来说是指指针本身常量属性的转化)

②实参到形参需要常量转化,只有唯一一种情况:给指涉到常量对象类型的指针或引用形参传递一个普通的实参对象

③实参到形参的转化通过数值提升

④实参到形参的转化需要算术或指针转化

⑤类类型的转化

9)除此之外,小于int的整数类型会被自动提升至int类型。在一次实参到形参的转化中,类类型的转化只能发生一次。

void love(short s)
{
    cout<<"I love short"<<endl;
}

void love(int i)
{
    cout<<"I love int"<<endl;
}

class Button
{
public:
    Button()=default;
    Button(const string &s):name(s){}
    Button(string &&s):name(move(s)){}
private:
    string name;
};

void love()
{
    love('c');  ///output:I love int  <pre name="code" class="cpp">   ///小于int的转化会被自动提升至int
Button bx("Ok"); ///由"OK"转化至string属于一次类类型转化,然后紧接着可以直接调用 ///Button(string &&)构造函数 Button by = "Reset"; ///错误,由"Reset"到string属于一次类类型转化 ///由于采用复制构造,因此需要将string再转化为Button才可以调用其复制 ///构造函数,故而需要两次类类型转化,而这是不被允许的 }


10)实参到形参的转化过程中如果有类类型转化参与,只有当两个函数提供相同的类类型转化时才考虑数值转化,否则调用出现歧义,主要是在写类的类型转化函数式需要注意,一般算术类型和类类型的转化只需提供一个转化函数,其余的转化可以自动进行。

class Demon
{
public:
    Demon()=default;
    Demon(int){}
    Demon(double){}

    operator int()const
    {
        return 0;
    }

    operator double()const
    {
        return 0.0;
    }
};

void love(long double d)
{

}

void love()
{
    Demon d;
    love(d);   ///错误,可通过不同的类类型转化得来,
    ///其后的算数转化优先级不再考虑,产生歧义
}

11)类的运算符重载需要考虑的重载函数既有类的成员函数,也有非成员函数

class Woman
{
public:
    bool operator==(const Woman &w)
    {
        return true;
    }
};


class Girl
{
public:
    bool operator==(const Girl &g)const
    {
        return true;
    }
};

bool operator==(const Girl &g,const Woman &w)
{
    cout<<"Woman isn't equal to Girl"<<endl;
    return false;
}

bool operator==(const Woman &w,const Girl &g)
{
    cout<<"Girl isn't equal to Woman"<<endl;
    return false;
}

void gw_equal()
{
    Girl ga,gb;
    Woman wa,wb;
    cout<<(ga == gb)<<endl;   ///output:1
    cout<<(wa == wb)<<endl;   ///output:1
    
    cout<<(ga == wa)<<endl;   
    ///output:
    ///Woman isn't equal to Girl
    ///0
    cout<<(wa == ga)<<endl;   
    ///output:
    ///Girl isn't equal to Woman
    ///0
}

C++总结之函数重载