小谈C++的函数重载,成员函数覆盖与成员函数隐藏

小谈C++的函数重载,成员函数覆盖与成员函数隐藏

 

函数重载:

函数重载是指在同一作用域内,可以有一组具有相同函数名,不同参数列表的函数,这组函数被称为重载函数。在一个类内,可以将功能相似的函数进行重载,一方面方便调用,另一方面使得类简洁。注意同一作用域不单单指类作用域内的成员函数,非成员的全局函数也可以进行重载。

其规则为:相同的函数名,不同的参数列表。不同的参数列表包括参数的类型不同,和参数的数量不同两种。注意不同的返回值类型无法实现函数重载。重载函数与virtual关键字无关。

       下面例子是一个函数重载的例子,包括类成员函数的重载和非成员函数重载。

      

#include <iostream>

#include <string>

 

using namespace std;

 

class Overload

{

public:

    Overload(){ adder = 0;}

    int add(int a);

    int add(int a, int b);

    string add(string a,string b);

private:

    int adder;

};

 

int Overload::add(int a)

{

    adder += a;

    return adder;

}

 

int Overload::add(int a, int b)

{

    return a + b;

}

 

string Overload::add(string a, string b)

{

    return a + b;

}

// 与void print()冲突,编译错误

//int print()

//{

//    cout << "Intprint!" << endl;

//}

 

void print()

{

    cout << "voidprint!" << endl;

}

 

void print(int para)

{

    cout << "Theint para value is : " << para << endl;

}

void print(string para)

{

    cout << "Thestring para value is : " << para << endl;

}

 

int main()

{

    Overload overload;

 

    print(overload.add(10));

    print(overload.add(10,10));

   print(overload.add("Hello ", "World!"));

 

    print();

    return 0;

}

      

       运行结果:

The int paravalue is : 10

The int paravalue is : 20

The string paravalue is : Hello World!

void print!

 

       上述代码中,add具有不同参数个数的重载函数,具有相同参数个数,不同参数类型的重载函数。注意:对于不同参数个数的重载函数,如果前面参数相同,而多出的参数不可以设置默认值,否则便也也是错误。如下例:

int add(int a);

int add(int a, int b = 0);

此时由于b有了默认值,对第二参数的调用则可以使用add(1)这样单个参数的形式进行调用,将产生歧义。

 

函数覆盖:

       覆盖特指的子类函数覆盖或改写父类虚函数的情况。

       1.不同的范围中(位于基类与派生类)

       2.函数名字相同

       3.参数相同

       4.基类函数必须是virtual函数

      

       这里实现的也就是C++中的动态绑定。将子类对象赋给父类指针或引用时,调用覆盖的函数,则调用子类的对应函数,实现动态绑定。而如果函数没有进行覆盖,则会调用父类的函数。如下实例进行说明:

#include <iostream>

using namespace std;

 

class Base

{

public:

    Base():a(1), b(2){};

    virtual void printa();

    void print();

    void printb();

protected:

    int a;

    int b;

};

 

void Base::printa()

{

    cout << "a :" << a << endl;

}

 

void Base::printb()

{

    cout << "Base->> ";

    cout << "b :" << b << endl;

    cout << "Baseprintb call print a ->>";

    printa();

}

 

void Base::print()

{

    cout << "Baseclass!" << endl;

}

 

class Derived : public Base

{

public:

    Derived(){a = 11; b = 12;}

    virtual void printa();

    void print(string b);

    void printb();

};

 

void Derived::printa()

{

    cout << "a :" << a << endl;

}

 

void Derived::printb()

{

    cout <<"Derived->> ";

    cout << "b :" << b << endl;

    cout <<"Derived printb call printa ->>";

}

 

void Derived::print(string b)

{

    cout <<"Derived class!--" << b << endl;

}

 

int main()

{

    Base * base = newDerived();

    base->printa();

    base->printb();

    base->print();

    //base->print("Hello world");

 

    return 0;

}

       运行结果:

a : 11

Base->> b: 12

Base printb callprint a ->>a : 11

Base class!

       解析:由于在基类中printa为虚函数,在子类中进行了覆盖,可以实现动态绑定,因此调用子类自己的printa函数,输出a : 11。

       对于printb,由于它没有声明为virtual,因此它不会实现动态绑定,因此调用的是基类的printb函数,又因为在对成员函数进行调用时,需要默认传入this指针作为参数,由于此时base指针指向的是Derived的对象,因此它传入的也是自己所指向的对象的this指针,因此在printb里面打印出了 Base-> b:12的结果。!!!注意:其实C++中对成员函数已经通过C++的名字修饰的处理,将他们变为了类似全局函数的函数,所不同的是如果在函数中使用到了成员,则会为函数添加this指针作为隐式参数!!!

       下面这一句则充分证明了虽然调用的是Base的printb,但是传入的是Derived的this指针,在printb中调用printa方法,由于此时传入的this指针为Derived对象的this指针,仍然发生动态绑定,打印的为a : 11

       最后一句无需解释,上述解析明白之后,这个自然明白。

       总结一句:如果使用virtual 关键字说明函数,则对函数的调用完全是根据调用时使用的类的类型来决定。如果用到了成员,则会想函数中传递this指针,this指针由当前的实际对象来决定。

      

       注:由于父类对象无法赋给子类指针或引用,这样的赋值时违反C++的规则的。因此不存在子类指针指向的父类对象,在调用函数时传递给函数的为父类对象的情况出现。其他两种情况:父类指针指向父类对象,子类指针指向子类对象都不存在上述的问题。此处不再额外阐述。

      

隐藏:

       这里的” 隐藏”是指派生类的函数屏蔽了与其同名的基类函数。

       C++中的隐藏规则:

       1.如果派生类的函数与基类函数名字相同,但是参数不同,不论有无virtual关键字,基类的函数将被隐藏。(注意不要与重载混淆)

       2.如果派生类的函数与基类函数同名,并且参数也相同,但是基类函数没有virtual关键字,此时基类的函数被隐藏。(注意不要与覆盖混淆)

      

       此处使用网上他人写的例子,没有记错应该是林锐的高质量C/C++编程中的代码。

       #include<iostream.h>

using namespace std;

class Base

{

public:

    virtual void f(float x){cout << "Base::f(float) " << x << endl;}

    void g(float x){ cout<< "Base::g(float) " << x << endl;}

    void h(float x){ cout<< "Base::h(float) " << x << endl;}

};

 

class Derived : public Base

{

public:

    virtual void f(float x){cout << "Derived::f(float) " << x << endl;}

    void g(int x){ cout<< "Derived::g(int) " << x << endl;}

    void h(float x){ cout<< "Derived::h(float) " << x << endl;}

};

 

int main(void)

{

    Derived d;

    Base *pb = &d;

    Derived *pd = &d;

 

    // 没有问题,动态绑定

    pb->f(3.14f); // Derived::f(float) 3.14

    pd->f(3.14f); // Derived::f(float) 3.14

 

    // 相同名字,但是不同参数,为隐藏

    pb->g(3.14f); // Base::g(float) 3.14

    pd->g(3.14f); // Derived::g(int) 3 (surprise!)

 

    // 相同名字与参数,但是没有virtual,为隐藏

    pb->h(3.14f); // Base::h(float) 3.14 (surprise!)

    pd->h(3.14f); // Derived::h(float) 3.14

 

    return 0;

}

       运行结果:

Derived::f(float)3.14

Derived::f(float)3.14

Base::g(float)3.14

Derived::g(int)3

Base::h(float)3.14

Derived::h(float)3.14

 

解析:

前两行没有异议,与上一节的覆盖相同。第三行和第四行,则是隐藏规则1中所描述的,派生类中出现了与父类中同名但是不同参数的函数,此时会将父类的同名函数隐藏掉。最后两行则是对没有virtual修饰的同名同参函数的说明。

 

注:假设注释掉子类中的g函数,同样的代码,第三和第四行的输出将相同。但是调用函数时传入的this指针不是同一个this指针。

 

By andy

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值