在c ++中,派生对象和基础对象之间有什么区别,
尤其是当班级中有虚拟功能时。
派生对象是否维护其他表来保存指针
到函数?
答案 0 :(得分:11)
答案 1 :(得分:2)
理论上,如果从另一个派生一个类,则有一个基类和一个派生类。如果创建派生类的对象,则会有派生对象。在C ++中,您可以多次从同一个类继承。考虑:
struct A { };
struct B : A { };
struct C : A { };
struct D : B, C { };
D d;
在d
对象中,每个A
对象中有两个D
个对象,称为“基类子对象”。如果您尝试将D
转换为A
,那么编译器会告诉您转换是不明确的,因为它不知道哪个 A
对象你想转换:
A &a = d; // error: A object in B or A object in C?
如果您命名A
的非静态成员,也是如此:编译器会告诉您一个模糊性。在这种情况下,您可以先转换为B
或C
A &a = static_cast<B&>(d); // A object in B
对象d
被称为“最派生对象”,因为它不是另一个类类型对象的子对象。为避免上述歧义,您可以继承虚拟
struct A { };
struct B : virtual A { };
struct C : virtual A { };
struct D : B, C { };
现在,只有一个<{1>}类型的子对象,即使你有两个子对象,这个对象包含在:subobject A
和子对象{ {1}}。将B
对象转换为C
现在不明确,因为D
和A
路径上的转换将产生相同的B
子对象。
以上是一个复杂的问题:从理论上讲,即使不考虑任何实现技术,C
和A
子对象中的任何一个或两个现在都不再是连续的。两者都包含相同的A对象,但两者都不包含彼此。这意味着其中一个或两个必须“拆分”并仅引用另一个的A对象,以便B
和C
对象可以具有不同的地址。在线性存储器中,这可能看起来像(假设所有对象的大小都是1字节)
B
C
是C: [1 byte [A: refer to 0xABC [B: 1byte [A: one byte at 0xABC]]]]
[CCCCCCC[ [BBBBBBBBBBCBCBCBCBCBCBCBCBCBCB]]]]
和CB
子对象所包含的内容。现在,如您所见,C
子对象将被拆分,没有办法,因为B
中没有C
,反之亦然。使用B
函数中的代码访问某些成员的编译器不能只使用偏移量,因为C
函数中的代码不知道它是否包含在子代码中object,或 - 当它不是抽象时 - 它是否是一个派生最多的对象,因此它旁边有C
个对象。
答案 2 :(得分:1)
public
冒号。 (我告诉过你C ++很讨厌)
class base { }
class derived : public base { }
答案 3 :(得分:1)
让我们:
class Base {
virtual void f();
};
class Derived : public Base {
void f();
}
没有f是虚拟的(在伪“c”中实现):
struct {
BaseAttributes;
} Base;
struct {
BaseAttributes;
DerivedAttributes;
} Derived;
虚拟功能:
struct {
vfptr = Base_vfptr,
BaseAttributes;
} Base;
struct {
vfptr = Derived_vfptr,
BaseAttributes;
DerivedAttributes;
} Derived;
struct {
&Base::f
} Base_vfptr
struct {
&Derived::f
} Base_vfptr
对于多重继承,事情变得更复杂:o)
答案 4 :(得分:0)
Derived是Base,但Base不是Derived
答案 5 :(得分:0)
base-是您派生的对象。 derived - 是继承父亲的公共(和受保护)成员的对象。
派生对象可以覆盖(或在某些情况下必须覆盖)他父亲的一些方法,从而创建不同的行为
答案 6 :(得分:0)
基础对象是派生其他人的基础对象。通常它会有一些虚拟方法(甚至是纯虚拟方法),子类可以覆盖它们来专门化。
基础对象的子类称为派生对象。
答案 7 :(得分:0)
派生对象派生自其基础对象。
答案 8 :(得分:0)
您是否询问相应对象在内存中的表示?
基类和派生类都有一个指向其虚函数的指针表。根据已覆盖的功能,该表中条目的值将发生变化。
如果B添加了更多不在基类中的虚函数,则B的虚方法表将更大(或者可能有一个单独的表,具体取决于编译器实现)。
答案 9 :(得分:0)
在c ++中,派生对象和基础对象之间有什么区别,
可以使用派生对象代替基础对象;它拥有基础对象的所有成员,也许还有更多自己的成员。因此,给定一个函数将引用(或指针)带到基类:
void Function(Base &);
您可以将引用传递给派生类的实例:
class Derived : public Base {};
Derived derived;
Function(derived);
尤其是当班级中有虚拟功能时。
如果派生类重写了虚函数,那么即使通过对基类的引用,也会始终在该类的对象上调用被覆盖的函数。
class Base
{
public:
virtual void Virtual() {cout << "Base::Virtual" << endl;}
void NonVirtual() {cout << "Base::NonVirtual" << endl;}
};
class Derived : public Base
{
public:
virtual void Virtual() {cout << "Derived::Virtual" << endl;}
void NonVirtual() {cout << "Derived::NonVirtual" << endl;}
};
Derived derived;
Base &base = derived;
base.Virtual(); // prints "Derived::Virtual"
base.NonVirtual(); // prints "Base::NonVirtual"
derived.Virtual(); // prints "Derived::Virtual"
derived.NonVirtual();// prints "Derived::NonVirtual"
派生对象是否维护其他表来保存指向函数的指针?
是 - 两个类都将包含一个指向虚函数表的指针(称为“vtable”),以便在运行时找到正确的函数。您不能直接访问它,但它确实会影响内存中数据的大小和布局。