【问题标题】:Virtual functions - base class pointer虚函数——基类指针
【发布时间】:2012-03-23 05:23:29
【问题描述】:

我明白为什么基类指针指向派生类对象。但是,我不明白为什么我们需要为它分配一个基类对象,而它本身就是一个基类对象。

谁能解释一下?

#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;
}

【问题讨论】:

  • 什么意思:为什么要给它赋值,一个基类对象能不能说的清楚一点。
  • @Rohit:上面例子中,在调用成员函数的时候,为什么是语句,p = &b,当p本身是一个基类指针,为什么不直接使用呢,比如:p ->函数()。我知道,我错了,但我需要纠正它。

标签: c++ class pointers virtual


【解决方案1】:

因为指针本身不能做任何事情。
指针必须指向一个有效的对象,以便您可以使用它。

为什么会有上述说法?

一步一步的解释也许会消除你的疑惑。

第 1 步:

base *p;

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

第 2 步:

p = &b;

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

第 3 步:

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

导出指针p 并在其指向的对象上调用vfunc() 方法。即:b.

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

【讨论】:

  • 我好像明白其中的症结了。能否请您多解释一下。
  • @user980411:我对它进行了更多更新以更具解释性。希望对您有所帮助。但实际上您确实需要获取good C++ book
  • 感谢您的简单解释。
【解决方案2】:

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

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

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

当我们编写myfunc 时,我们既不知道也不关心所涉及对象的确切身份——我们只关心它知道如何在命令中使用dosomething

就您所展示的编写代码而言,我们直接将基类或派生类的对象的地址分配给指针,这更像是例外而不是规则。你说的很对,在这种情况下,我们并没有真正从使用指向基址的指针来引用派生对象中获得很多。主要的好处是像函数这样的东西,当我们编写代码时,派生类可能还不存在,但只要派生类符合规定的接口,它就可以工作。

【讨论】:

  • 感谢您的真诚努力。
【解决方案3】:

不强制调用

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();
}

【讨论】:

  • 谢谢。 p[0] = new base(); 行会返回什么?
  • @user980411 它会给你基类对象的指针
  • 某种意义上的指针,会new base()创建一个base类型的对象,并将其地址返回给p[0]...???
【解决方案4】:

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

请参阅上述书中如何在 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();
}
}

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-03-04
    • 1970-01-01
    • 2010-11-15
    • 2018-11-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-08
    相关资源
    最近更新 更多