我理解为什么要使基类指针指向派生类对象。但是,我无法理解为什么我们需要分配它,一个基类对象,当它本身是一个基类对象时。
有人可以解释一下吗?
#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;
}
答案 0 :(得分:3)
因为指针本身无法做任何事情 指针必须指向有效对象,以便您可以使用它。
为什么上述声明?
一步一步的解释也许可以清除你的怀疑。
第1步:
base *p;
创建一个指针p
,它可以存储类base
的对象的地址。但它没有初始化它指向内存中的任何随机地址。
第2步:
p = &b;
将有效base
对象的地址分配给指针p
。 p
现在包含此对象的地址。
第3步:
p->vfunc(); // access base's vfunc()
取消指针p
并在方法指向的对象上调用方法vfunc()
。即:b
。
如果您删除第2步,则您的代码只会尝试取消引用未初始化的指针,并会导致 Undefined Behavior &amp;很可能是崩溃。
答案 1 :(得分:1)
无需拨打电话
p = &b;
p->vFunc();
你可以直接打电话
b.vFunc();
两者都会给你相同的结果。
然而,似乎你应该了解虚函数的强大功能。假设您要存储10个base
或derived1
个对象的实例并重复调用该函数,您将如何做到这一点?如果要将其传递给公共函数,或者将其存储在数组中?
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