我现在已经在学校编程了一段时间,我正在开展我的第一个独立大型项目。我一直在发现很多关于编程的东西,这些东西我以前都不知道,而且很棒。
然而,越来越多,我觉得我不再把C ++理解为一种语言,我越深入研究它。在继续之前,我想直接得到关于引用和指针的一些(错误)概念,我希望你,stackoverflow,如果我错了,你可以纠正我。
提前致谢,我们走了!
1。在类之间进行转换时,实际转换的是虚拟表。
示例:
class A{
public:
A() : x(0) {};
int x;
virtual void doStuff() {
cout << x <<endl;
}
};
class B : public A{
public:
B() : y(1) {};
int y;
virtual void doStuff() {
cout << y <<endl;
}
};
如果我将类型B的对象b转换为A,那么内部会发生什么,b的虚拟表将被丢弃,并被替换为类型A的相应虚拟表,并且y的析构函数将被调用,因为不再提及它了。类似地,b中的doStuff将指向A :: doStuff的函数地址而不是B :: doStuff。但是,指向x的地址将保持不变。
2。这意味着利用多态性的唯一方法是通过指针和引用
作为第1点的结果,在类的虚方法中利用多态的唯一方法是使用引用和指针,因为如果我们通过值传递,类本身将自动转换为基类。
示例:
void doALotOfStuff(A a1, A a2) {
a1.doStuff();
a2.doStuff();
}
int main(){
A a;
B b;
doALotOfStuff(a,b);
return 0;
}
会打印出来
0
0
因为编译器会生成将b转换为A的代码。
第3。此外,利用数组和STL容器的这种多态性的唯一方法是使用指针,因为无法存储引用
由于向量不可分配,因为引用不可分配,所以如果我想要一个基类向量,我需要创建一个A的指针向量,以保留类型的元素的虚拟表乙
很抱歉,如果这是TL; DR,但是当我在我的项目上做一些类设计时,我有点困扰我,并且我意识到我无法真正摆脱使用指针或引用,因为库接口和多态问题。
答案 0 :(得分:3)
虽然你在#1点有一些不错的想法,但实际上并不是它的工作原理。转换不是就地完成的,它是通过创建一个新对象来完成的,该对象从转换源复制它知道的成员(也就是说,基类成员,除非你的基类中有一些非常奇怪的前向声明) )。派生对象的实例不会以任何方式更改。当然,副本具有与原始对象不同的内存地址。通过值传递总是涉及复制。
答案 1 :(得分:3)
当您说“将b
类型的B
转换为A
”时,您可能意味着两件事:
A
创建复制类型b
的新对象,或A*
的指针或指向A&
的{{1}}类型的引用。在任何一种情况下,原始对象b
都保持不变。您可以在转换后将其丢弃,但这是一个不同的问题,仅转换不会改变b
。
(除非在极少数情况下,转换运算符或所涉及的构造函数对b
采用非const引用并更改参数。但是,即使在这种情况下,它也不是您提到的vtable替换。)
一旦创建了一个对象,它的类型在整个生命周期内都不会改变(直到它被销毁)。这意味着,你提到的vtable替换永远不会发生。
是的, C ++中的多态性是通过指针或引用来实现的。
请注意,在您的示例中,a1和a2是从a和b复制的,转换不会以任何方式改变a或b。
是的,你是对的。或者任何类型的智能指针都可以接受(或者更可取)。
答案 2 :(得分:0)
你所说的是正确的,有必要有指针来利用多态性。如您所述,它全部绑定在虚函数表中。
在第1点中,您谈到从B类转换为A'。这有点困惑,因为更准确地说,将使用适当的组件创建新对象。请参阅关于切片的下一点。
在第2点中,您演示的是切片的原理。这是派生类删除派生部分并且仅保留基础的地方。
需要注意的一点是,您应该在STL容器中使用boost :: shared_ptr或boost :: unique_ptr,否则内存管理会成为一个危险的问题。
如果您没有听说过提升,可以查看here这是非常宝贵的资源。
答案 3 :(得分:0)
你正在混淆传值和传递参考。
当您按值传递值时,就像在示例中一样,您正在复制旧实例中的数据,并创建新实例。旧实例不会被破坏。该函数将在新实例上运行,并且不会对您传入的实例进行操作。
当您按值传递并传递派生类型时,可能会发生对象切片: http://en.wikipedia.org/wiki/Object_slicing
当您通过引用传递时,不会创建新实例,并且该函数将在您的现有实例上运行。引用的类型并不重要,除了它限制了函数可以对您的对象执行的操作。它仍在您的对象上运行,并调用派生的实现。
为了论证,传递指针是传值(复制指针)和传递引用(你不复制指针引用的对象)之间的混合。
由于所有这些,进行多态的唯一方法(基于接口,但是使用派生实现)是通过指针或引用。
是的,STL容器无法使用引用。
您可以使用Boost中的智能指针来解决引用的一些限制,并解决一些“哑”指针的恶化:)
答案 4 :(得分:0)
你的实际混淆来自声明1.对象永远不会在C ++中转换 - 类型的对象总是这种类型的对象(除了在构造对象的基类时的一些小的幕后怪异性我们不会谈论)。而是转换VALUES,当您将VALUE从一种对象类型转换为另一种对象类型时,实际发生的是您正在使用旧对象中的数据构建一个NEW对象。
因此,在您的2示例中,当您调用doALotOfStuff
时,编译器将调用构造函数A::A(const B &)
来创建一个新的A作为第二个参数a2
传递。它还会调用构造函数A::A(const A &)
为a1
创建新的A,因此a1
和a2
与a
和b
完全不同。 }。