为何使用虚拟功能?

har*_*ris 43 c++ virtual-functions

可能重复:
有人可以解释C++虚拟方法吗?

我有一个关于C++虚函数的问题.

我们为什么以及何时使用虚拟功能?任何人都可以给我一个实时实现或使用虚拟功能?

Alo*_*ave 56

如果要覆盖派生类的某个行为(读取方法)而不是为基类实现的行为(并且希望在运行时通过指向基类的指针)执行此操作,则可以使用虚函数.

经典的例子是当你有一个被调用的基类Shape和从中派生出来的具体形状(类).每个具体类都会覆盖(实现一个虚方法)Draw().

类层次结构如下:

类层次结构

以下代码段显示了示例的用法; 它创建了一个Shape类指针数组,其中每个指针都指向一个不同的派生类对象.在运行时,调用该Draw()方法会导致调用该派生类重写的方法,并Shape绘制(或呈现)特定的方法.

Shape *basep[] = { &line_obj, &tri_obj,
                   &rect_obj, &cir_obj};
for (i = 0; i < NO_PICTURES; i++)
    basep[i] -> Draw ();
Run Code Online (Sandbox Code Playgroud)

上面的程序只使用指向基类的指针来存储派生类对象的地址.这提供了松散耦合,因为如果shape随时添加新的具体派生类,则程序不必急剧改变.原因是实际使用(依赖)具体Shape类型的代码段很少.

以上是着名的SOLID设计原则的开放闭合原理的一个很好的例子.

  • 如果我们不使用`virtual`并在子类中重新定义它们,例如`Line`和`Triangle`,该怎么办?那会有什么不同? (8认同)
  • 如果函数在基类中不是虚函数,则无法通过基类指针访问派生类版本. (4认同)
  • 抱歉,这里还是有点困惑。如果您在运行时之前不知道需要哪个版本的函数,但在此之前的某个时间您还需要基类的实例化,那么说虚函数是否正确?因为看起来我仍然可以在没有虚拟函数的情况下度过,并且在运行时之前不知道我需要什么,只需创建子类并在我知道后实例化正确的类。所以看起来*您还需要在*之前的某个时间实例化基类是至关重要的,对吗?又名:通过基指针调用派生函数 (2认同)

Vol*_*dyi 24

当您需要以相同方式处理不同对象时,可以使用虚函数.它被称为多态性.让我们假设您有一些基类 - 类似于经典形状:

    class Shape
    {
        public:
           virtual void draw() = 0;
           virtual ~Shape() {}
    };

    class Rectange: public Shape
    {
        public:
            void draw() { // draw rectangle here } 
    };


    class Circle: public Shape
    {
        public:
           void draw() { // draw circle here }
    };
Run Code Online (Sandbox Code Playgroud)

现在你可以拥有不同形状的矢量:

    vector<Shape*> shapes;
    shapes.push_back(new Rectangle());
    shapes.push_back(new Circle());
Run Code Online (Sandbox Code Playgroud)

你可以画出这样的形状:

    for(vector<Shape*>::iterator i = shapes.begin(); i != shapes.end(); i++)
    {
          (*i)->draw();
    }
Run Code Online (Sandbox Code Playgroud)

通过这种方式,您可以使用一个虚拟方法绘制不同的形状 - draw().根据有关指针后面对象类型的运行时信息选择正确版本的方法.

注意 当您使用虚函数时,您可以将它们声明为虚函数(如在类Shape中,只需在方法proto之后放置"= 0").在这种情况下,您将无法使用纯虚函数创建对象实例,它将被称为Abstract类.

在析构函数之前还要注意"虚拟".如果您计划通过指向其基类的指针来处理对象,则应该声明析构函数为virtual,因此当您为基类指针调用"delete"时,将调用所有析构函数链并且不会发生内存泄漏.


hol*_*gac 21

想想动物类,从它衍生出来的是猫,狗和牛.动物类有一个

virtual void SaySomething()
{
    cout << "Something";
}
Run Code Online (Sandbox Code Playgroud)

功能.

Animal *a;
a = new Dog();
a->SaySomething();
Run Code Online (Sandbox Code Playgroud)

狗不应该打印"Something",而应该说"Bark",猫应该说"Meow".在这个例子中,你看到a是一只狗,但有时候你有一个动物指针并且不知道它是哪种动物.你不想知道它是哪种动物,你只想让动物说些什么.所以你只需要调用虚函数,猫会说"喵",狗就会说"吠".

当然,SaySomething函数应该是纯虚拟的,以避免可能的错误.

  • 这么晚才回复很抱歉.考虑到这一点,开发人员创建了一个继承我们的动物类的新类(例如,`Fox`)并忘记覆盖SaySomething方法.如果你使用我提供的虚拟方法,`Fox`实例会说"Something",这是不对的.如果我们将SaySomething声明为纯虚拟,我们将无法实例化`Fox`,包含`new Fox(...)`的代码将引发错误.这样,创建`Fox`类的开发人员将在编译时通知他/她的错误.编译时错误很好,因为它们不浪费时间:) (4认同)
  • 谢谢你的回答令人满意.... (2认同)