虚函数 - 基类指针

时间:2012-03-23 05:23:29

标签: c++ class pointers virtual

我理解为什么要使基类指针指向派生类对象。但是,我无法理解为什么我们需要分配它,一个基类对象,当它本身是一个基类对象时。

有人可以解释一下吗?

#include <iostream>
using namespace std;
class base {
     public:
        virtual void vfunc() {
            cout << "This is base's vfunc().\n";
         }
};
 class derived1 : public base {
      public:
            void vfunc() {
                 cout << "This is derived1's vfunc().\n";
    }
};
int main()
{
     base *p, b;
     derived1 d1;
     // point to base
     p = &b;
     p->vfunc(); // access base's vfunc()
     // point to derived1
     p = &d1;
     p->vfunc(); // access derived1's vfunc()
     return 0;
}

4 个答案:

答案 0 :(得分:3)

因为指针本身无法做任何事情 指针必须指向有效对象,以便您可以使用它。

为什么上述声明?

一步一步的解释也许可以清除你的怀疑。

第1步:

base *p;

创建一个指针p,它可以存储类base的对象的地址。但它没有初始化它指向内存中的任何随机地址。

第2步:

p = &b;

将有效base对象的地址分配给指针pp现在包含此对象的地址。

第3步:

p->vfunc(); // access base's vfunc() 

取消指针p并在方法指向的对象上调用方法vfunc()。即:b

如果您删除第2步,则您的代码只会尝试取消引用未初始化的指针,并会导致 Undefined Behavior &amp;很可能是崩溃。

答案 1 :(得分:1)

无需拨打电话

p = &b;
p->vFunc();

你可以直接打电话

b.vFunc();

两者都会给你相同的结果。

然而,似乎你应该了解虚函数的强大功能。假设您要存储10个basederived1个对象的实例并重复调用该函数,您将如何做到这一点?如果要将其传递给公共函数,或者将其存储在数组中?

vase *p[4];

base b1;
derived d;

p[0] = new base();

p[1] = &b1;

p[2] = new dervied1();    

p[3] = &d;

for (int i =0 ;i <4 ;i++)
{
  p[i]->vFunc();
}

答案 2 :(得分:1)

我不是完全确定我完全理解你的问题,但使用虚函数的典型的情况是我们编写一个函数,并希望它能够工作使用基类的对象或从中派生的任何东西:

struct base { 
    virtual void dosomething() = 0;
};

void myfunc(base *b) {
    b->dosomething();
}

当我们写myfunc时,我们既不知道也不关心所涉及对象的确切身份 - 我们只关心它知道如何dosomething命令。

就像你所显示的编写代码一样,我们直接将基类或派生类的对象的地址分配给指针,这比规则更为例外。你是对的,在这种情况下,我们并没有真正从使用指向base的指针获得很多东西来引用派生的对象。主要的好处是类似于一个函数,当我们编写代码时,派生类可能甚至不存在,但只要派生类符合规定的接口,它就会起作用。

答案 3 :(得分:-2)

当您想要应用设计模式时,这种类型的引用非常适用(您可能需要通过面向对象设计的高级课程,或者从阅读书开始:首先,设计模式,我建议)

参见上面提到的书中如何在java中实现装饰器模式。

public abstract class Beverage {
String description = "Unknown Beverage";
public String getDescription() {
    return description;
}
public abstract double cost();   
}

然后认为我们将espresso和DarkRoast作为两个子类:

public class Espresso extends Beverage {

public Espresso() {
    this.description = "Espresso";
}

public double cost() {
    return 1.99;
}
}

public class DarkRoast extends Beverage {
public DarkRoast() {
    description = "Dark Roast Coffee";
}

public double cost() {
    return .99;
}
}

现在,我们将添加装饰器:

public abstract class CondimentDecorator extends Beverage {
public abstract String getDescription();
 }

并建立一些具体的装饰者:

public class Mocha extends CondimentDecorator {

Beverage beverage;

public Mocha(Beverage beverage) {
    this.beverage = beverage;
}

public String getDescription() {
    return beverage.getDescription() + ", Mocha";
}

public double cost() {

    return .20 + beverage.cost();
}
} 

和另一个包装器:

public class Whip extends CondimentDecorator {
Beverage beverage;

public Whip(Beverage beverage) {
    this.beverage = beverage;
}

public String getDescription() {
    return beverage.getDescription() + ", Whip";
}

public double cost() {
    return .10 + beverage.cost();
}
}

最后,看看main函数中发生了什么,以及我们如何利用指向父类的指针:

public static void main(String[] args) {
    Beverage beverage = new Espresso();
    System.out.println(beverage.getDescription() + " $" + beverage.cost());
    Beverage beverage2 = new DarkRoast();
    beverage2 = new Mocha(beverage2);
    beverage2 = new Mocha(beverage2);
    beverage2 = new Whip(beverage2);
    System.out.println(beverage2.getDescription() + " $" + beverage2.cost());

你能猜出输出是什么吗?好:

Espresso $1.99
Dark Roast Coffee, Mocha, Mocha, Whip $1.49