【C++11】final, override,重载,重写覆盖,重写隐藏

本文详细解析了C++中的final和override关键字的作用与用法,包括如何使用final禁止类的继承或成员函数的重写,以及如何使用override确保正确地重写了基类的虚函数。同时,还介绍了成员函数的重载、重写隐藏和重写覆盖的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1:final, override的作用

1:final的作用:修饰类或成员函数

修饰类时: 表示本类禁止被继承;
修饰成员函数:
    virtual成员函数:表示不允许子类重写覆盖,但可以重写隐藏
    非virtual成员函数:表示不允许子类重写隐藏;

2:override:用于标示虚函数,明确说明这是重写覆盖父类的同名函数,避免造成重写隐藏的情况;

2: 示例代码

/ final的用法: 用于标识类或成员函数,禁止子类继承或重写
// 具体是禁止重写隐藏还是重写覆盖得看成员函数是否有virtual关键字修饰
// 1, final 修饰类
// 用final修饰,意味着继承该类会导致编译错误
class Hello final 
{
    ...
}

/*
 2, final 修饰成员方法,可以是虚函数,也可以是普通成员函数,
 虚函数则表示不允许子类重写覆盖,但经测试证明仍允许重写隐藏,
 普通成员函数则表示不允许子类重写隐藏:
*/
class A
{
public:
    virtual void f(int) final {}
};

class B : public A
{
public:
    /*
    这样将会导致错误,因为父类声明了该虚函数为final,
    即表示不允许子类重写覆盖;
    */
    // virtual void f(int){}

    /*
    正确,这个函数与上面那个函数是互相独立的,不被认为是重写覆盖, 
    而是重写隐藏,这种情况要好好注意
    */
    virtual void f(){} 
    virtual void f(double){} // 正确,原因同上
};
// 2 override的用法
// override只能用于标识虚函数,不能是普通成员函数, 比如:
class A
{
public:
    virtual void f(int){}
    void g(){};
};

class B : public A
{
public:
    /*
    明确表示这个函数为的是重写覆盖父类的f(int);虚函数的, 
    如果可能发生错误导致可能的重写隐藏的话编译时就报错
    */
    virtual void f(int) override {} 

    /*
    像这种形式就会导致编译时报错,因为这样不是重写覆盖,
    而是重写隐藏,重写覆盖要求函数签名是一样的;
    */
    // virtual void f() override {} 

    /*
    错误,因为override只能用于修饰虚函数,g();不是虚函数,
    不可以使用override;
    */
    // void g() override {} 

};

另一方面,如果需要一个函数永远不能被重写覆盖(顺着继承层次往下都不能被重写覆盖),可以把该函数标识为final,在基类中和派生类中都可以这么做。如果是在派生类中,我们可以同时使用override和final标识符。

class B 
{
public:
   virtual void f(int) 
   {
       std::cout << "B::f" << std::endl;
   }
};

class D : public B
{
public:
   virtual void f(int) override final 
   {
       std::cout << "D::f" << std::endl;
   }
};

class F : public D
{
public:
   virtual void f(int) override
   {
       std::cout << "F::f" << std::endl;
   }
};

3:区分重载(overload)、重写隐藏、和重写覆盖(override) 注:

参考资料来源:http://blog.csdn.net/dazhong159/article/details/7844369
注: 原文单纯的区分重载、隐藏、和覆盖,有点混乱,这里我做了个人的注解:
成员函数中分为重载(overload)和重写(override)
而重写包括重写隐藏重写覆盖(常说override更多指的是这个)

类成员函数的重载(overload)、重写隐藏、和重写覆盖(override)区别
a.成员函数被重载(overload)的特征:
 (1)相同的范围(在同一个类中);
 (2)函数名字相同;
 (3)参数不同;
 (4)virtual关键字可有可无。
 (5)对(1)的补充:不同类中形成的两种重载(overload):
     5.1 如下代码: 
// 5.1
class B 
{
public:
   virtual void f(short) 
   {
       std::cout << "B::f" << std::endl;
   }
};

class D : public B
{
public:
   virtual void f(int) 
   {
       std::cout << "D::f" << std::endl;
   }
};

// 5.2
class B 
{
public:
   virtual void f(int) const 
   {
       std::cout << "B::f " << std::endl;
   }
};

class D : public B
{
public:
   virtual void f(int) 
   {
       std::cout << "D::f" << std::endl;
   }
};
b.重写覆盖(override)是指派生类函数覆盖基类函数,特征是:
 (1)不同的范围(分别位于派生类与基类);
 (2)函数名字相同;
 (3)参数相同;整个函数签名一样;
 (4)基类函数必须有virtual关键字。
// 重写覆盖示例
class A
{
public:
    void f(int)  
    {
        cout << "A" << endl;
    }
};

class B : public A
{
public:
    virtual void f(int){}; 
};
c.重写“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下:
 (1)如果派生类的函数与基类的函数同名,但是参数不同。
    此时,不论有无virtual关键字,基类的函数将被隐藏
   (注意别与重载混淆)。但注意如果加上virtual则
   通过父类型指针或引用访问不用类名::的形式访问也可以访问;
   如下例:
 (2)如果派生类的函数与基类的函数同名,并且参数也相同,
     但是基类函数没有virtual关键字。此时,
     基类的函数被隐藏(注意别与覆盖混淆)
// 重写隐藏示例       
#include <iostream>
#include <cstring>

using namespace std;

class A
{
public:
    void hello(){cout<<"A hello"<<endl;}
    virtual void hello(int i){cout<<"A hello int"<<endl;}
};

class B:public A
{
public:
    // B中的hello()隐藏了A中的hello(int)
    void hello() const{cout<<"B hello"<<endl;} 
};

// const导致重载时的隐藏问题,解决办法,在B中重新定义hello(int)函数
int main()
{
    B b;
    b.hello();
    // b.hello(1); // error
    b.A::hello(1);  // 这种情况得通过类名::的形式访问

    A *p = new B;
    // 通过父类指针类型来访问还是正确,这种情况不用类名::的形式访问
    p->hello(2); 
} 
// 注意分清楚重载(overload)和重写隐藏以及重写覆盖(override)
// 重写隐藏:
    class A
    {
    public:
        void test(){};
    }
    class B : public A
    {
    public:

    /* 
    因为不是虚函数,同名则会导致重写隐藏父类的,这是要调用子类中
    要调用父类的test()必须通过A::test();的形式进行调用;
    */
        void test(int i){} 
    };


    /*
    另外注意:
        一个函数中是否有const修饰函数也会造成重载,但不属于重写隐藏
        或重写覆盖,是两个不同的函数, 如下例:
    */
    class B
    {
    public:
        virtual void test()const{};
    };
    class D : public B
    {
    public:
        virtual void test(){}; 
    /*
    这个函数没有const修饰,因此这个函数尽管有多态的性质,
    但是是属于两个函数,这是因为成员函数的隐形参数this的原因:
    一个是this,一个是const this的类型;
    */
    };

小结:

1)分清楚overload, 和override;
2)注意重载与重写覆盖,重写隐藏的条件;

参考资料:
http://blog.jobbole.com/44015/
http://blog.csdn.net/luyafei_89430/article/details/38662491
http://blog.csdn.net/dazhong159/article/details/7844369

<think>嗯,用户问的是关于C++中使用override说明符时未正确重写基类方法的问题。首先,我得回忆一下override关键字的用途。它是在C++11引入的,用来显式指明派生类中的函数是重写基类的虚函数。这样编译器可以帮助检查是否正确重写,避免一些潜在的错误。 那用户遇到的问题是,当使用override时,编译器报错说方法没有正确重写基类的函数。这时候可能的错误原因有很多。首先想到的是函数签名是否完全一致。比如,基类中的函数参数类型、数量、顺序是否和派生类中的完全一致。包括const修饰符和引用限定符这些细节,如果基类函数有const,而派生类函数没有,或者反过来,都会导致重写失败。 然后,基类中的函数是否被声明为虚函数。如果基类的函数不是虚函数,那么派生类用override重写就会出错。这时候需要检查基类中的函数是否有virtual关键字,或者在C++11之后是否使用了final,导致无法重写。 还有访问权限的问题。如果基类的虚函数是private,而派生类试图重写但访问权限不同,可能也会有问题。不过一般来说,访问权限不影响重写,只是影响能否访问,所以可能不是主要原因,但需要确认。 另外,基类和派生类的函数是否在同一个作用域。比如,如果基类的函数在某个命名空间中,而派生类没有正确引入,可能导致无法找到基类函数,从而无法重写。或者存在隐藏(name hiding)的情况,派生类中的同名函数隐藏了基类的函数,这时候需要用using声明来引入基类的函数。 还有可能是基类函数被删除或者不可访问,比如基类的析构函数如果是私有的,可能导致派生类无法正确继承,但这种情况可能比较少见。 接下来,解决方案方面。首先检查函数签名是否完全一致,包括返回类型。在C++中,返回类型必须相同或者是协变的(比如基类返回基类指针,派生类返回派生类指针)。如果返回类型不匹配,也会导致重写失败。例如,基类返回void,派生类返回int,这样加上override就会报错。 然后确认基类函数是否为虚函数。如果基类函数没有virtual,或者被声明为final,那么派生类无法重写。需要给基类函数添加virtual关键字,或者移除final限制。 检查访问权限,确保派生类有权限访问基类的虚函数。虽然访问权限不影响重写,但如果派生类无法访问基类的虚函数,那么实际上也无法重写,因为无法覆盖一个不可访问的函数。 另外,检查是否存在多继承或虚继承带来的问题。如果基类是通过虚继承引入的,可能需要特别处理构造函数,但这里可能更多是函数重写的问题,而非构造。 如果基类使用了模板或者函数重载,派生类可能需要明确指定要重写的函数,避免歧义。例如,使用using声明来引入基类的特定重载版本,或者在派生类中使用基类名限定符。 还有,编译器的问题。不同编译器可能有不同的表现,尤其是在较旧的版本中,可能对override的支持不完全,需要确认编译器是否支持C++11或更高标准,并在编译选项中开启相应的标准支持,比如-std=c++11或更高。 例如,用户提供的引用[2]中提到,如果某个类不包含虚函数,一般不用虚析构函数,否则会增加虚函数表和虚指针的体积。这可能和基类的设计有关,如果基类没有虚析构函数,而用户试图在派生类中重写,但基类析构函数非虚,这时候用override会报错。 另外,引用[4]提到含有纯虚函数的类是抽象类,不能生成对象。如果基类中的虚函数是纯虚的,那么派生类必须实现它,否则派生类仍然是抽象类。如果用户试图在派生类中用override重写纯虚函数,但函数签名不匹配,就会导致错误,同时派生类还是抽象类,无法实例化。 总结可能的原因:签名不匹配、基类函数非虚或final、访问权限问题、作用域或隐藏问题、返回类型不协变、编译器选项不支持C++11等。 解决方案步骤:逐项检查函数签名,添加virtual,调整访问权限,使用using引入基类函数,确保编译器支持C++11及以上,检查基类是否被正确继承等。 测试案例的话,可以构造一个基类,其中有一个虚函数,然后在派生类中用override重写,故意制造签名错误,看编译器报错信息,再修正后是否能通过。 例如: class Base { public: virtual void func(int) const {} }; class Derived : public Base { public: void func(int) override {} // 错误:缺少const修饰符 }; 这里Derived的func缺少const,导致无法重写,编译器会报错。修正后添加const,就能正确重写。 或者基类函数参数是int,派生类用了double,也会导致签名不匹配。 另一个例子是基类函数非虚: class Base { public: void func() {} // 非虚函数 }; class Derived : public Base { public: void func() override {} // 错误:基类函数非虚 }; 此时override会报错,因为基类func不是虚函数。 或者基类函数是private,但派生类试图重写: class Base { private: virtual void func() {} }; class Derived : public Base { public: void func() override {} // 可以重写,但访问权限是public,但基类private可能导致外部无法通过基类指针调用 }; 这里虽然可以重写,但访问权限的问题可能影响使用,但override本身不会报错,因为访问权限在重写之后检查。 不过用户的问题更可能在于函数签名或基类虚函数是否存在,所以需要重点检查这些方面。</think>### C++中`override`说明符未正确重写基类方法的错误原因及解决方案 #### 错误原因分析 1. **函数签名不匹配** 派生类方法与基类虚函数的**参数列表、返回类型、const修饰符或引用限定符**不一致。例如: ```cpp class Base { public: virtual void func(int x) const; // 基类函数带有const修饰符 }; class Derived : public Base { public: void func(int x) override; // 错误:缺少const修饰符[^3] }; ``` 2. **基类方法未声明为虚函数** 若基类方法未使用`virtual`关键字,或已被`final`禁止重写,派生类无法用`override`重写: ```cpp class Base { public: void func(); // 非虚函数 }; class Derived : public Base { public: void func() override; // 错误:基类方法非虚[^2] }; ``` 3. **访问权限冲突** 基类虚函数可能为`private`,而派生类试图以更高权限(如`public`)重写,但C++允许此操作,需检查是否误用作用域。 4. **作用域或名称隐藏问题** 若基类函数被派生类的其他同名函数隐藏,需使用`using`显式引入基类函数: ```cpp class Base { public: virtual void func(int); }; class Derived : public Base { public: using Base::func; // 引入基类函数 void func(int) override; }; ``` 5. **编译器标准未支持C++11或更高** `override`是C++11特性,需确保编译选项包含`-std=c++11`或更高。 --- #### 解决方案 1. **严格匹配函数签名** 检查参数类型、返回类型(需协变或相同)、`const`/`volatile`修饰符及引用限定符(如`&&`)。 2. **确保基类方法为虚函数** 若基类方法非虚,需添加`virtual`关键字,或检查是否被`final`禁止重写。 3. **调整访问权限或使用`using`声明** 若存在名称隐藏,通过`using Base::func;`显式引入基类方法。 4. **验证编译器设置** 启用C++11及以上标准(如`-std=c++17`)。 5. **检查多继承或虚继承的影响** 虚基类的构造函数需由最远子类调用[^1],但函数重写需确保派生类正确继承基类。 --- #### 测试案例 ```cpp // 正确示例 class Base { public: virtual void calculate(int x) const; }; class Derived : public Base { public: void calculate(int x) const override; // 签名完全匹配 }; ``` ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值