C++ 在C+中隐藏、重载或覆盖+;

C++ 在C+中隐藏、重载或覆盖+;,c++,inheritance,member-hiding,C++,Inheritance,Member Hiding,然后我又有一个问题。大概是这样的: #include <iostream> using namespace std; class Base { public: void foo() { cout<<"Base."<<endl; } }; class Derive:public Base { public: void foo() { cout<<"Derive."<&l

然后我又有一个问题。大概是这样的:

#include <iostream>
using namespace std;

class Base
{
public:
    void foo()
    {
        cout<<"Base."<<endl;
    }
};

class Derive:public Base
{
public:
    void foo()
    {
        cout<<"Derive."<<endl;
    }
};

int main()
{
    Derive d;
    Base *pb=&d;    //attention here
    pb->foo();      //ateention here
    system("pause");
    return 0;
}
class Base
{
public:
    virtual void foo() // add virtual
    {
        cout<<"Base."<<endl;
    }
};
#包括
使用名称空间std;
阶级基础
{
公众:
void foo()
{

coutFunction
Base::foo
是非虚拟函数。调用它是因为使用了指向基类的指针

如果您这样更改代码:

#include <iostream>
using namespace std;

class Base
{
public:
    void foo()
    {
        cout<<"Base."<<endl;
    }
};

class Derive:public Base
{
public:
    void foo()
    {
        cout<<"Derive."<<endl;
    }
};

int main()
{
    Derive d;
    Base *pb=&d;    //attention here
    pb->foo();      //ateention here
    system("pause");
    return 0;
}
class Base
{
public:
    virtual void foo() // add virtual
    {
        cout<<"Base."<<endl;
    }
};
类基
{
公众:
虚拟void foo()//添加虚拟
{

您是否可以看到这种行为,因为
foo()在 BASE>代码中未声明为<代码>虚拟<代码>。C++中,默认情况下,成员函数是非虚的。为了利用动态调度和多态性,必须明确声明函数为“代码>虚拟<代码>。

代码中没有超载或重写。<代码> BAS::FoO < /代码)>正在被调用,而不是

derivate::foo
,因为程序员没有为名称
foo
使用动态绑定的任何规范。如果没有提供虚拟说明符,编译器将根据调用该函数的对象的静态类型而不是它可能指向的类型来查找该函数这称为静态绑定,在编译时完成

如果使用虚拟说明符,则会在运行时查找函数名,并根据对象的运行时类型调用函数名



另一方面,您的基类需要一个虚拟析构函数,原因与上面所述的相同。如果您有一个指向
derivate
base类指针,删除该指针只会调用基类析构函数,而不会同时调用基类和派生类。

因为
foo
不是虚拟的,所以调用的函数是based表示静态类型(即指针声明指向的类型),而不是动态类型(指针当前引用的对象类型)

也有一些棘手的情况需要考虑。一个点(其中一些其他答案实际上有点误导)是,它实际上不是函数名称,而是整个函数签名。例如:

#include <iostream>

struct base { 
    virtual void foo() { 
        std::cout << "base::foo";
    }
};

struct derived : base { 
    virtual void foo() const { 
        std::cout << "derived::foo";
    }
};

int main(){ 
    base *b = new derived;

    b->foo();
}
这样,如果函数签名有差异(如此处所示),编译器将生成一条错误消息,提醒您,
derived::foo
标记为
override
,但实际上并不重写基类中的函数。但是,这是在C++11中添加的,因此如果您使用的是旧编译器,则可能无法实现此功能(尽管幸运的是,没有实现它的编译器很快就被遗忘了)

将基类中的签名更正为:

virtual void foo() const // ...

…将让代码编译并产生正确的结果。

从您问题的标题判断,我认为您不完全理解函数何时被隐藏、重载和覆盖

示例代码1:

struct Base
{
   void foo()
   {
   }
};

struct Derive: public Base
{
   void foo()
   {
   }
};

int main()
{
   Derive d;
   Base *pb=&d;
   d.foo();    // Resolves to Derived::foo()
   pb->foo();  // Resolves to Base::foo()
   return 0;
}
struct Base
{
   void foo(int i)
   {
   }
};

struct Derive: public Base
{
   void foo()
   {
   }
};

int main()
{
   Derive d;
   Base *pb=&d;
   d.foo();      // Resolves to Derived::foo()
   d.foo(10);    // Compiler error.

   pb->foo(10);  // Resolves to Base::foo(int)
   pb->foo();    // Compiler error.
   return 0;
}
struct Base
{
   void foo()
   {
   }
};

struct Derive: public Base
{
   void foo()
   {
   }
   void foo(int )
   {
   }
};

int main()
{
   Derive d;
   d.foo();      // Resolves to Derived::foo()
   d.foo(10);    // Resolves to Derived::foo(int)

   Base *pb=&d;
   pb->foo();    // Resolves to Base::foo()
   pb->foo(10);  // Compiler error.

   return 0;
}
struct Base
{
   virtual void foo()
   {
   }
   void foo(int)
   {
   }
};

struct Derive: public Base
{
   void foo()
   {
   }
   void foo(int )
   {
   }
};

int main()
{
   Derive d;
   d.foo();      // Resolves to Derived::foo()
                 // But Derived:foo() gets called at run time.

   d.foo(10);    // Resolves to Derived::foo(int)
                 // But Derived:foo(int) gets called at run time.

   Base *pb=&d;
   pb->foo();    // Resolves to Base::foo()
                 // But Derived:foo() gets called at run time due to
                 // function overwritting.

   pb->foo(10);  // Resolves to Base::foo(10)
                 // Base:foo(int) gets called at run time.

   return 0;
}
为什么
d.foo()
调用
Derived::foo()
pb->foo()
调用
Base::foo()

这个问题的答案在于编译器解决这些函数绑定所采取的步骤

给定类型为T的对象和函数名
f
,编译器在
T
中查找名为
f
的函数。如果只找到一个名为
f
的函数,则搜索函数停止。如果找到多个函数,则尝试从
T
中找到的函数集中进行重载解析。

如果在
T
中未找到任何名为
f
的函数且
T
有基类,则在
T
的基类中尝试上述逻辑。如果
T
没有任何基类,则编译器报告错误

来到示例代码的对象

当处理函数调用
d.foo()
时,编译器在
Derived
中查找
foo
。它在那里找到一个匹配项并停止。由于
Derived::foo()
不是
虚拟的
函数,因此绑定在编译时完成。在运行时调用
Derived::foo()

当处理函数调用
pb->foo()
时,编译器在
Base
中查找
foo
。它在那里找到一个匹配项并停止。由于
Base::foo()
不是
虚拟的
函数,因此绑定在编译时完成。在运行时调用
Base::foo()

示例代码2:

struct Base
{
   void foo()
   {
   }
};

struct Derive: public Base
{
   void foo()
   {
   }
};

int main()
{
   Derive d;
   Base *pb=&d;
   d.foo();    // Resolves to Derived::foo()
   pb->foo();  // Resolves to Base::foo()
   return 0;
}
struct Base
{
   void foo(int i)
   {
   }
};

struct Derive: public Base
{
   void foo()
   {
   }
};

int main()
{
   Derive d;
   Base *pb=&d;
   d.foo();      // Resolves to Derived::foo()
   d.foo(10);    // Compiler error.

   pb->foo(10);  // Resolves to Base::foo(int)
   pb->foo();    // Compiler error.
   return 0;
}
struct Base
{
   void foo()
   {
   }
};

struct Derive: public Base
{
   void foo()
   {
   }
   void foo(int )
   {
   }
};

int main()
{
   Derive d;
   d.foo();      // Resolves to Derived::foo()
   d.foo(10);    // Resolves to Derived::foo(int)

   Base *pb=&d;
   pb->foo();    // Resolves to Base::foo()
   pb->foo(10);  // Compiler error.

   return 0;
}
struct Base
{
   virtual void foo()
   {
   }
   void foo(int)
   {
   }
};

struct Derive: public Base
{
   void foo()
   {
   }
   void foo(int )
   {
   }
};

int main()
{
   Derive d;
   d.foo();      // Resolves to Derived::foo()
                 // But Derived:foo() gets called at run time.

   d.foo(10);    // Resolves to Derived::foo(int)
                 // But Derived:foo(int) gets called at run time.

   Base *pb=&d;
   pb->foo();    // Resolves to Base::foo()
                 // But Derived:foo() gets called at run time due to
                 // function overwritting.

   pb->foo(10);  // Resolves to Base::foo(10)
                 // Base:foo(int) gets called at run time.

   return 0;
}
为什么编译器会在这里产生错误

当处理函数调用
d.foo(10)
时,编译器在
Derived
中查找
foo
。它在那里找到一个匹配项并停止。它尝试使用该函数,但函数的签名与调用代码不匹配。因此,这是一个编译器错误

当处理函数调用
pb->foo()
时,编译器在
Base
中查找
foo
。它在那里找到一个匹配项并停止。它尝试使用该函数,但函数的签名与调用代码不匹配。因此,这是一个编译器错误

一旦编译器在
Derived
中找到
foo
,它就不会在
Base
中搜索匹配的
foo

在这种情况下,您可以认为
Derived::foo
完全隐藏
Base::foo

示例代码3:

struct Base
{
   void foo()
   {
   }
};

struct Derive: public Base
{
   void foo()
   {
   }
};

int main()
{
   Derive d;
   Base *pb=&d;
   d.foo();    // Resolves to Derived::foo()
   pb->foo();  // Resolves to Base::foo()
   return 0;
}
struct Base
{
   void foo(int i)
   {
   }
};

struct Derive: public Base
{
   void foo()
   {
   }
};

int main()
{
   Derive d;
   Base *pb=&d;
   d.foo();      // Resolves to Derived::foo()
   d.foo(10);    // Compiler error.

   pb->foo(10);  // Resolves to Base::foo(int)
   pb->foo();    // Compiler error.
   return 0;
}
struct Base
{
   void foo()
   {
   }
};

struct Derive: public Base
{
   void foo()
   {
   }
   void foo(int )
   {
   }
};

int main()
{
   Derive d;
   d.foo();      // Resolves to Derived::foo()
   d.foo(10);    // Resolves to Derived::foo(int)

   Base *pb=&d;
   pb->foo();    // Resolves to Base::foo()
   pb->foo(10);  // Compiler error.

   return 0;
}
struct Base
{
   virtual void foo()
   {
   }
   void foo(int)
   {
   }
};

struct Derive: public Base
{
   void foo()
   {
   }
   void foo(int )
   {
   }
};

int main()
{
   Derive d;
   d.foo();      // Resolves to Derived::foo()
                 // But Derived:foo() gets called at run time.

   d.foo(10);    // Resolves to Derived::foo(int)
                 // But Derived:foo(int) gets called at run time.

   Base *pb=&d;
   pb->foo();    // Resolves to Base::foo()
                 // But Derived:foo() gets called at run time due to
                 // function overwritting.

   pb->foo(10);  // Resolves to Base::foo(10)
                 // Base:foo(int) gets called at run time.

   return 0;
}
当处理函数调用
d.foo()
d.foo(10)
时,编译器在
Derived
中查找
foo
。它在那里找到两个匹配项并停止。然后它尝试重载解析。它能够找到两个版本的匹配项。因为
Derived::foo()都没有
s是一个
虚拟
函数,绑定在编译时完成

当处理函数调用
bp->foo()
bp->foo(10)
时,编译器