我有点困惑:
- 如果我有一个基类 A 和一个扩展 A 的类 B,A 类型的变量可以保存 B 类型的值,反之亦然?
如果是,为什么?即使 B 从 A 派生,它们不是完全不同吗?类型安全性如何?
- 如果这是可能的,我在使用它时需要注意什么?这在性能方面将如何发挥作用?
注意:对不起,如果我问了太多问题,请忽略它们,并注意那些带有列表装饰点的“标记”:) 另外,这不是我的作业。我是一名业余程序员,并且具有使用 OOP 编写脚本语言的技能,但我对 C++ 中的 OOP 输入相对较新。
如果你这样做:
B b;
A a = b;
然后你得到“切片”。 a
将只A
包含b
.
但是你可以有参考/指针:
B b;
A &ra = b;
A *pa = &b;
在这种情况下,ra
只需pa
引用/指向真实B
对象。这是因为公共继承对 IS-A 关系建模。使用更具描述性的名称更容易理解。把A
和想Animal
成B
。IS-ABaboon
因此通过使用引用/指针,您可以将 a视为更通用的类型。Baboon
Animal
Baboon
Animal
并非相反,但性能在这里不是正确的问题。如果您决定要进行 OO 设计,请不要为了性能问题而牺牲正确的抽象。过早的优化是万恶之源。祝你好运!
A 类型的变量可以保存 B 类型的值(对于“持有”的某些定义,正如其他答案所解释的那样),但反之亦然。打破一个标准的例子:
class Animal {};
class Dog : public Animal {};
class Cat : public Animal {};
这是合法的,因为Cat
源自Animal
:
Cat c;
Animal& a = c;
这不是:
Animal a;
Cat& c = a;
这是类型安全的,因为您已将其定义为这样;继承的全部意义在于允许这种事情发生,因此您可以继续调用泛型Animal
变量上的方法,而无需知道其中存储了哪个基类。至于性能问题,调用虚方法比较慢,因为必须在运行时决定实际调用哪个方法(Cat::foo()
与 相比Dog::foo()
,例如,取决于Animal
存储在变量中的特定类型)——这称为动态调度. 对于非虚拟方法,决策可以在编译时发生
指向类对象的指针或引用可以A
指向/引用类对象B
。那是因为如果B
派生自A
,则 a B
is-a A
。类的每个对象都具有类B
的所有成员变量和函数A
,以及类独有的那些B
。
class A
{
public:
virtual ~A();
void foo();
};
class B : public A
{
public:
void bar();
};
A * a = new B;
a->foo(); // perfectly valid, B inherits foo() from A
//a->bar(); // compile-time error, bar() is only defined in B
delete a; // make sure A has a virtual destructor!
通常,当您想通过虚函数利用多态行为时,会使用这种代码。