2012-03-23 148 views
3

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

任何人都可以解释一下吗?

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

你是什么意思:**为什么我们需要分配给它,基类对象**你可以更清楚一点。 – 2012-03-23 05:26:27

+0

@Rohit:在上面的例子中,当调用成员函数时,为什么当p本身是一个基类指针时,语句p =&b,为什么不直接使用它,如:p-> func()。我知道,我错了,但我需要纠正它。 – user980411 2012-03-23 05:29:40

回答

3

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

为什么上述说法?

一步一步的解释可能会清除你的疑问。

步骤1:

base *p; 

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

步骤2:

p = &b; 

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

步骤3:

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

Dererences指针p和调用由它指向的对象上的方法vfunc()。即:b

如果删除第2步:,你的代码只是试图取消引用一个未初始化的指针,并会导致Undefined Behavior &最有可能崩溃。

+0

我似乎理解**的关键**。你能否多解释一下。 – user980411 2012-03-23 05:28:11

+0

谢谢。这有帮助。 – user980411 2012-03-23 05:32:11

+1

@ user980411:我对它进行了更新,使其更具说明性。希望有帮助。但实际上你需要选择一个[良好的C++书](http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list)。 – 2012-03-23 05:37:07

1

不强制调用

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

可以直接调用

b.vFunc(); 

都将给你相同的结果。

不过看起来你应该明白虚拟功能的威力。假设您想要存储basederived1对象的10个实例并重复调用该函数,那么您将如何执行该操作?或者如果你想将它传递给一个通用函数,将它存储在数组中之后?

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

谢谢。行** p [0] = new base(); ** return? – user980411 2012-03-23 05:41:01

+1

@ user980411它会给你基类对象的指针 – Jeeva 2012-03-23 05:42:00

+0

指针在这个意义上,会** new base()**创建一个类型为base的对象,并返回它的地址给p [0] ... ??? – user980411 2012-03-23 05:45:37

1

我不完全知道我理解你的问题很好,但典型情况下使用虚拟函数的是,当我们编写一个函数,并希望它与基地的目标努力类或任何从它派生:

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

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

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

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

+0

感谢您的真诚努力。 – user980411 2012-03-23 05:44:45

-2

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

请参阅例如如何在上述书中实现java中的装饰器模式。

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

则认为我们有咖啡和在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 
+1

你为什么要用java代码回答一个C++问题? – 2012-10-20 20:24:14