derived 类 的切片是如何发生的?
how slicing of derived classes occurs?
我无法理解切片是如何发生的?例如,在这段代码中:
class A {
public:
virtual h() {
cout << "type A" << endl;
}
};
class B : public A {
public:
virtual h() override {
cout << "type B" << endl;
}
};
void f(A a) {
a.h();
}
void g(A& a) {
a.h();
}
int main() {
A a1 = B(); // a1 is A and doesn't recognize any B properties
A *a = new B();
f(*a);
g(*a);
}
我注意到:
变量 a1 不知道它是 B,但变量 a 知道。我指的是发生这种情况是因为在变量 a1 中,对 B 的赋值是按值进行的,与变量 a 不同,我在其中创建了一个指向 B 的指针。
当我将变量 a 传递给不同的函数时会发生同样的事情 - 当我按值传递时,它认为它是 A,但当我按引用传递时,它认为它是 B。
如果有人能给我更广泛和更深入的解释,我将很高兴。提前致谢!
在此声明中
A a1 = B();
使用了 class A 的默认复制构造函数,因为对象 a1
的类型为 A。
这个构造函数看起来像
constexpr A( const A & );
因此只有 class B
对象的 A
类型的子对象被复制到对象 a1
。指向对象 a1
的虚函数指针的 table 将包含指向它自己的虚函数的指针。
在此声明中
A *a = new B();
指针a
指向一个B
类型的对象,它包含table个指向虚函数的指针,而虚函数又包含一个指向[的虚函数的指针=55=]B.
所以在这次通话中
g(*a);
传递了对动态类型B
对象的引用(通过引用传递给函数时对象本身没有改变)
所以在函数内
void g(A& a) {
a.h();
}.
会有访问table的虚函数指针,其中包含指向classB的虚函数的指针。这里是class的新对象A
未创建,因为原始对象是通过引用传递的。
在本次通话中
f(*a);
函数的参数是按值传递的。那就是声明函数的参数有一个复制初始化,如
void f(A a) {
a.h();
}
其实和声明中的情况是一样的
A a1 = B();
如上所述。
- variable a1 doens't know it's a B
更正确:变量a1被声明为A,所以它是A。它不是B,也从来不是B。这与变量是什么无关"knows";这是关于变量的类型。 a1 由 "slicing" 基本子对象的副本从 B 初始化。
I would be happy if anyone could give me more extensive and deeper explanation.
运行时多态需要间接。 A 类型的对象始终是 A 类型,没有其他类型。这就是语言中类型的简单方式。原因之一是编译器必须知道对象需要多少内存。如果编译器为 A 保留内存,那么一个可能更大的派生实例如何适合保留内存?
但是指向 A 的指针(或引用)可以指向不同的 A 对象,也可以指向从 A 派生的 class 的基础子对象。这无关紧要完整的对象有多大,指针可以指向对象的一部分,而不会影响指针本身的大小。
how slicing of derived classes occurs?
只要将派生对象转换为基类型,就会发生切片。转换复制基础 class 子对象。请注意,当您将派生值转换为对基的引用时,不会发生切片 - 除非您使用该引用来初始化基类型的对象。
我无法理解切片是如何发生的?例如,在这段代码中:
class A {
public:
virtual h() {
cout << "type A" << endl;
}
};
class B : public A {
public:
virtual h() override {
cout << "type B" << endl;
}
};
void f(A a) {
a.h();
}
void g(A& a) {
a.h();
}
int main() {
A a1 = B(); // a1 is A and doesn't recognize any B properties
A *a = new B();
f(*a);
g(*a);
}
我注意到:
变量 a1 不知道它是 B,但变量 a 知道。我指的是发生这种情况是因为在变量 a1 中,对 B 的赋值是按值进行的,与变量 a 不同,我在其中创建了一个指向 B 的指针。
当我将变量 a 传递给不同的函数时会发生同样的事情 - 当我按值传递时,它认为它是 A,但当我按引用传递时,它认为它是 B。
如果有人能给我更广泛和更深入的解释,我将很高兴。提前致谢!
在此声明中
A a1 = B();
使用了 class A 的默认复制构造函数,因为对象 a1
的类型为 A。
这个构造函数看起来像
constexpr A( const A & );
因此只有 class B
对象的 A
类型的子对象被复制到对象 a1
。指向对象 a1
的虚函数指针的 table 将包含指向它自己的虚函数的指针。
在此声明中
A *a = new B();
指针a
指向一个B
类型的对象,它包含table个指向虚函数的指针,而虚函数又包含一个指向[的虚函数的指针=55=]B.
所以在这次通话中
g(*a);
传递了对动态类型B
对象的引用(通过引用传递给函数时对象本身没有改变)
所以在函数内
void g(A& a) {
a.h();
}.
会有访问table的虚函数指针,其中包含指向classB的虚函数的指针。这里是class的新对象A
未创建,因为原始对象是通过引用传递的。
在本次通话中
f(*a);
函数的参数是按值传递的。那就是声明函数的参数有一个复制初始化,如
void f(A a) {
a.h();
}
其实和声明中的情况是一样的
A a1 = B();
如上所述。
- variable a1 doens't know it's a B
更正确:变量a1被声明为A,所以它是A。它不是B,也从来不是B。这与变量是什么无关"knows";这是关于变量的类型。 a1 由 "slicing" 基本子对象的副本从 B 初始化。
I would be happy if anyone could give me more extensive and deeper explanation.
运行时多态需要间接。 A 类型的对象始终是 A 类型,没有其他类型。这就是语言中类型的简单方式。原因之一是编译器必须知道对象需要多少内存。如果编译器为 A 保留内存,那么一个可能更大的派生实例如何适合保留内存?
但是指向 A 的指针(或引用)可以指向不同的 A 对象,也可以指向从 A 派生的 class 的基础子对象。这无关紧要完整的对象有多大,指针可以指向对象的一部分,而不会影响指针本身的大小。
how slicing of derived classes occurs?
只要将派生对象转换为基类型,就会发生切片。转换复制基础 class 子对象。请注意,当您将派生值转换为对基的引用时,不会发生切片 - 除非您使用该引用来初始化基类型的对象。