名字隐藏和脆弱的基础问题

Pup*_*ppy 15 c++ oop inheritance

我已经看到它说C++隐藏了名称,以减少脆弱的基类问题.但是,我绝对不知道这有多大帮助.如果基类引入了以前不存在的函数或重载,它可能与派生类引入的函数或对全局函数或成员函数的非限定调用冲突 - 但我没有看到的是重载的不同之处.为什么虚拟函数的重载应该与其他函数区别对待?

编辑:让我再告诉你一些我正在谈论的内容.

struct base {
    virtual void foo();
    virtual void foo(int);
    virtual void bar();
    virtual ~base();
};
struct derived : base {
    virtual void foo();
};

int main() {
    derived d;
    d.foo(1); // Error- foo(int) is hidden
    d.bar(); // Fine- calls base::bar()
}
Run Code Online (Sandbox Code Playgroud)

在这里,foo(int)区别对待bar(),因为它是一个过载.

Mik*_*our 7

我假设通过"脆弱的基类",你的意思是一种情况,基类的更改可能会破坏使用派生类的代码(这是我在维基百科上找到的定义).我不确定虚拟函数与此有什么关系,但我可以解释隐藏有助于避免这个问题.考虑以下:

struct A {};

struct B : public A
{
    void f(float);
};

void do_stuff()
{
    B b;
    b.f(3);
}
Run Code Online (Sandbox Code Playgroud)

函数调用do_stuff调用B::f(float).

现在假设某人修改了基类,并添加了一个函数void f(int);.没有隐藏,这将是函数参数的更好匹配main; 你要么改变了do_stuff(如果新函数是公共的)的行为,要么导致编译错误(如果它是私有的),而不改变它的do_stuff任何一个或任何直接依赖.通过隐藏,您没有更改行为,只有在使用using声明明确禁用隐藏时才能进行此类破坏.


Mat*_* M. 2

我认为虚函数的重载与常规函数的重载没有任何区别。但可能会有一个副作用。

假设我们有一个 3 层的层次结构:

struct Base {};

struct Derived: Base { void foo(int i); };

struct Top: Derived { void foo(int i); }; // hides Derived::foo
Run Code Online (Sandbox Code Playgroud)

当我写的时候:

void bar(Derived& d) { d.foo(3); }
Run Code Online (Sandbox Code Playgroud)

Derived::foo无论真实(运行时)类型是什么,该调用d都会静态解析为 。

然而,如果我随后引入virtual void foo(int i);in Base,那么一切都会改变。突然Derived::fooTop::foo成为覆盖,而不是仅仅将名称隐藏在各自的基类中的重载。

这意味着d.foo(3);现在不是直接静态解析为方法调用,而是解析为虚拟调度。

因此Top top; bar(top)将调用Top::foo(通过虚拟调度),它之前调用的是Derived::foo.

这可能并不理想。它可以通过显式限定 call 来修复d.Derived::foo(3);,但这确实是一个不幸的副作用。

当然,这主要是一个设计问题。只有当签名兼容时才会发生这种情况,否则我们将隐藏名称,并且不会覆盖;因此,有人可能会争辩说,对非虚拟函数进行“潜在”覆盖无论如何都会带来麻烦(不知道是否存在任何警告,它可能需要一个警告,以防止陷入这种情况)。

注意:如果我们删除 Top,那么引入新的虚拟方法就完全没问题,因为所有旧的调用都已由 Derived::foo 处理,因此只有新代码可能会受到影响

virtual不过,在基类中引入新方法时,尤其是当受影响的代码未知(交付给客户端的库)时,需要记住这一点。

请注意,C++0x 具有override检查方法是否确实是基虚拟方法的重写的属性;虽然它不能解决眼前的问题,但将来我们可能会想象编译器会对“意外”覆盖(即未标记为此类的覆盖)发出警告,在这种情况下,可以在引入后在编译时捕获此类问题虚拟方法。