C++ 继承中方法调用总结

前言

C++ 的继承和多态无疑是一个非常有用的特性,但在实际使用过程中,有一些新手十分容易犯的错误,这里做个整理和总结,列举了继承下各种函数声明及实际调用情况。

类说明

用于演示的类如下:

class Father{
public:
    void speak();
};

class Son : public Father{
public:
    void speak();
};

测试代码如下:

int main(){
    Father f = Father();
    f.speak();
    Son s = Son();
    s.speak();
    Father* actual_son = new Son();
    actual_son->speak();
    return 0;
}

默认覆写

class Father{
public:
    void speak(){
        cout << "Father speak" << endl;
    }
};

class Son : public Father{
public:
    void speak(){
        cout << "Son speak" << endl;
    }
};

// 执行结果:
// Father speak
// Son speak
// Father speak

对于没有任何关键字声明的普通函数,同类型(同名同参数)的子类函数会覆盖父类的函数。这里的覆盖具体是对外隐藏父类的 speak() ,在子类内部仍可以调用父类的 speak() 函数,通过 Father::speak() 调用。

由于不是虚函数,所以就失去了多态的特性,即使实际的对象是子类,调用的仍然是父类的方法。这是因为方法不是虚函数,调用接口是静态绑定的,其调用的方法取决于其指针的类型。

Father* actual_son = new Son();
actual_son->speak();
Son* p = static_cast<Son*>(actual_son);
p->speak();

// 对象本身并没有改变,所使用的指针不同,调用的方法不同
// 输出:
// Father speak
// Son speak

虚函数

class Father{
public:
    virtual void speak(){
        cout << "Father speak" << endl;
    }
};

class Son : public Father{
public:
    void speak() override{
        cout << "Son speak" << endl;
    }
};

// 执行结果:
// Father speak
// Son speak
// Son speak

如果声明为虚函数,就实现了多态,其执行的结果取决于对象本身。

由于虚函数有一个默认实现,所以即使子类没有覆写该虚函数,也不会报错,仍能正常执行。但有时候,我们既想要求子类必须重写该方法,又想提供一个默认实现,此时该如何实现呢?

纯虚函数

class Father{
public:
    virtual void speak() = 0;
};

void Father::speak(){
    cout << "Father speak" << endl;
}

class Son : public Father{
public:
    void speak() override{
        cout << "Son speak" << endl;
    }
};

class Son2 : public Father{
public:
    void speak() override{
        Father::speak();
    }
};

在这种情况下,Father 中的 speak() 是一个纯虚函数,子类必须重写该方法,但如果只需要默认实现,该纯虚函数还拥有一个默认实现,可以通过显式调用的方式调用。

override 关键字

在本例中,似乎 override 关键字并没有什么作用,即使去掉也没什么影响。但是在继承中,有一些容易犯的错误(但可能难以察觉)通过 override 关键字可以避免。

子类重写父类的方法,有以下条件需要满足:

  • 父类方法是虚函数
  • 方法返回类型兼容
  • 方法名、形参列表、常量属性和引用限定符相同

如果不满足其中一项,则无法完成重写,这个函数会作为一个新的函数定义,而无法实现多态。通过 override 关键字,编译器会自动检查该函数所对应的虚函数,如果找不到则报错。

final 关键字

虽然在本例中没有使用到,但如果想禁止一个方法被子类重写,就可以通过 final 关键字实现。

上一篇 下一篇

评论 | 0条评论