为什么我们实际上有虚函数?

Vij*_*jay 11 c++ overriding virtual-functions redefinition

我是C++的新手.

任何人都可以告诉我c ++中方法重写和虚函数概念之间的区别.

虚函数的功能可以在其派生类中被覆盖.在派生类中重新定义函数称为函数重写.

为什么我们实际上有虚函数?

Jon*_*onH 12

虚函数/方法只是一个函数,其行为可以通过重新定义函数的工作方式(使用相同的签名)在子类(或C++术语,派生类)中覆盖.

想想具有说话功能的基类哺乳动物.这个功能是无效的,只是简单地讲述哺乳动物的说话方式.当你从这个类继承时,你可以覆盖说话方法,这样狗就可以去"Arf Arf!" 和猫去"喵喵".

你的问题似乎在问什么是差异,没有,因为虚函数可以覆盖这些函数的行为.您可能会追踪覆盖函数之间的区别并重载它们.

重载函数意味着创建一个具有相同名称但不同参数的函数,即不同的参数数量和类型.以下是IBM网站上C++重载的解释:

重载(仅限C++)如果为同一范围中的函数名称或运算符指定了多个定义,则表示您已重载该函数名称或运算符.重载函数和运算符分别在重载函数(仅限C++)和重载运算符(仅限C++)中进行了描述.

重载声明是一个声明,声明与同一作用域中先前声明的声明具有相同的名称,但两个声明都有不同的类型.

如果调用重载的函数名称或运算符,编译器会通过将用于调用函数或运算符的参数类型与定义中指定的参数类型进行比较来确定要使用的最合适的定义.选择最合适的重载函数或运算符的过程称为重载解析,如过载解析(仅限C++)中所述.

至于需要虚函数的情况的完全合理的原因,这篇博文给出了一个很好的理由:http://nrecursions.blogspot.in/2015/06/so-why-do-we-need-virtual-functions. HTML

  • 一个小的挑剔:你说重载"意味着创建一个具有相同名称但不同参数或返回类型的函数".我只是删除该语句的"返回类型"部分...它们不被视为重载解析的一部分,至少在当前的c ++标准中. (2认同)

Tho*_*ews 9

函数重写和virtual函数之间的区别对于多态性变得很重要.特别是在使用引用或指向基类的指针时.

基本设置

在C++中,任何派生类都可以传递给需要基类对象的函数.(另请参见切片LSP).鉴于:

struct Base_Virtual
{
  virtual void some_virtual_function();
};

struct Base_Nonvirtual
{
  void some_function();
};

void Function_A(Base_Virtual * p_virtual_base);
void Function_B(Base_Nonvirtual * p_non_virtual_base);
Run Code Online (Sandbox Code Playgroud)

在上面的代码中,有两个基类,一个声明一个虚方法,另一个声明一个非虚函数.

声明了两个函数,它们需要指向相应基本clases的指针.

派生类

现在让我们测试多态性,尤其virtual是非虚拟(覆盖方法).结构:

struct Derived_From_Virtual
: public Base_Virtual
{
  void some_virtual_function(); // overrides Base_Virtual::some_virtual_function()
};
Run Code Online (Sandbox Code Playgroud)

struct Derived_From_Nonvirtual:public Base_Nonvirtual {void some_function(); }

根据C++语言,我可以传递指向to的指针Derived_From_Virtual,Function_A因为Derived_From_Virtual继承自Base_Virtual.我也可以通过一个指针Derived_From_NonvirtualFunction_B.

区别virtual和重写的区别

virtual在修改Base_Virtual,告诉编译器Function_A将使用Derived_From_Virtual::some_virtual_function()替代的方法Base_Virtual.这是因为该方法是虚拟的,最终定义可以驻留在将来派生类中.实际定义说在包含定义的大多数派生类中使用该方法.

将指针传递Derived_From_Nonvirtual给时Function_B,编译器将指示函数使用基类的方法Base_Nonvirtual::some_function().的some_function()派生类的方法是一个单独的,不相关的,从基类方法.

virtual多态性之间的主要区别在于覆盖和覆盖.


Any*_*orn 8

查看C++ FAQ lite,http://www.parashift.com/c++-faq-lite/.可能是初学者最好的C++资源之一.它有关于虚函数和覆盖的深入描述.

我个人发现C++ FAQ是我学习C++的最佳来源.其他人有不同意见,您的里程可能会有所不同

  • C++ FAQ不是介绍性的,它是一个参考. (4认同)
  • @John Dibling,也许你把C++ FAQ lite与C++ FQA混淆了,前者是comp.lang.c ++中常见问题的汇编,而后者只是对基于可疑事实的语言的咆哮. (3认同)

pyo*_*yon 3

抽象的

在本文中,我们讨论 C++ 中的虚函数。第零部分解释了如何声明和重写虚函数。第一部分尝试(可能失败)解释虚拟函数是如何实现的。第二部分是一个示例程序,它使用第 0 部分和第 1 部分中定义的示例类。第三部分是每个虚函数中给出的经典动物例子——多态性教程。

零部分

类的方法当且仅当其声明为虚拟方法时才被称为虚拟方法。

class my_base
{
public:
            void non_virtual_test() { cout << 4 << endl; } // non-virtual
    virtual void virtual_test()     { cout << 5 << endl; } // virtual
};
Run Code Online (Sandbox Code Playgroud)

(当然,我假设程序员以前没有做过类似的事情#define virtual。)

重新声明并重新实现其基之一的非虚拟方法的类被称为重载该方法。重新声明并重新实现其基之一的虚拟方法的类被称为重写该方法。

class my_derived : public my_base
{
public:
    void non_virtual_test() { cout << 6 << endl; } // overloaded
    void virtual_test()     { cout << 7 << endl; } // overriden
};
Run Code Online (Sandbox Code Playgroud)

第一部分

当编译器检测到某个类具有虚拟方法时,它会自动将虚拟方法表(也称为vtable)添加到该类的内存布局中。结果类似于编译此代码生成的结果:

class my_base
{
//<vtable>
// The vtable is actually a bunch of member function pointers
protected:
    void (my_base::*virtual_test_ptr)();
//</vtable>

// The actual implementation of the virtual function
// is hidden from the rest of the program.
private:
    void virtual_test_impl() { cout << 5 << endl; }

// Initializing the real_virtual_test pointer in the vtable.
public:
    my_base() : virtual_test_ptr(&my_base::virtual_test_impl) {}

public:
    void non_virtual_test() { cout << 4 << endl; }
    // The interface of the virtual function is a wrapper
    // around the member function pointer.
    inline void virtual_test() { *virtual_test_ptr(); }
};
Run Code Online (Sandbox Code Playgroud)

当编译器检测到某个类已重写虚拟方法时,它会替换 vtable 中与其关联的条目。结果类似于编译此代码生成的结果:

class my_derived : public my_base
{
// The actual implementation of the virtual function
// is hidden from the rest of the program.
private:
    void virtual_test_impl() { cout << 7 << endl; }

// Initializing the real_virtual_test pointer in the vtable.
public:
    my_derived() : virtual_test_ptr(&my_derived::virtual_test_impl) {}

public:
    void non_virtual_test() { cout << 6 << endl; }
};
Run Code Online (Sandbox Code Playgroud)

第二部分

既然已经清楚虚函数是使用 vtable 实现的,vtable 只不过是一堆函数指针,那么这段代码的作用应该很清楚:

#include <iostream>

using namespace std;

class my_base
{
    public:
            void non_virtual_test() { cout << 4 << endl; }
    virtual void virtual_test()     { cout << 5 << endl; }
};

class my_derived : public my_base
{
public:
    void non_virtual_test() { cout << 6 << endl; }
    void virtual_test()     { cout << 7 << endl; }
}

int main()
{
    my_base* base_obj = new my_derived();

    // This outputs 4, since my_base::non_virtual_test() gets called,
    // not my_derived::non_virtual_test().
    base_obj->non_virtual_test();

    // This outputs 7, since the vtable pointer points to
    // my_derived::virtual_test(), not to my_base::virtual_test().
    base_obj->virtual_test();

    // We shall not forget
    // there was an object that was pointed by base_obj
    // who happily lived in the heap
    // until we killed it.
    delete base_obj;

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

第三部分

因为如果没有动物的例子,虚拟函数的例子就不完整......

#include <iostream>

using namespace std;

class animal
{
public:
    virtual void say_something()
    { cout << "I don't know what to say." << endl
           << "Let's assume I can growl." << endl; }

    /* A more sophisticated version would use pure virtual functions:
     *
     * virtual void say_something() = 0;
     */
};

class dog : public animal
{
public:
    void say_something() { cout << "Barf, barf..." << endl; }
};

class cat : public animal
{
public:
    void say_something() { cout << "Meow, meow..." << endl; }
};

int main()
{
    animal *a1 = new dog();
    animal *a2 = new cat();
    a1->say_something();
    a2->say_something();
}
Run Code Online (Sandbox Code Playgroud)