直接得到关于指针和参考的概念

时间:2010-08-31 01:01:00

标签: c++ pointers polymorphism reference

我现在已经在学校编程了一段时间,我正在开展我的第一个独立大型项目。我一直在发现很多关于编程的东西,这些东西我以前都不知道,而且很棒。

然而,越来越多,我觉得我不再把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,但是当我在我的项目上做一些类设计时,我有点困扰我,并且我意识到我无法真正摆脱使用指针或引用,因为库接口和多态问题。

5 个答案:

答案 0 :(得分:3)

虽然你在#1点有一些不错的想法,但实际上并不是它的工作原理。转换不是就地完成的,它是通过创建一个新对象来完成的,该对象从转换源复制它知道的成员(也就是说,基类成员,除非你的基类中有一些非常奇怪的前向声明) )。派生对象的实例不会以任何方式更改。当然,副本具有与原始对象不同的内存地址。通过值传递总是涉及复制。

答案 1 :(得分:3)

  1. 当您说“将b类型的B转换为A”时,您可能意味着两件事:

    • A创建复制类型b的新对象,或
    • 制作指向A*的指针或指向A&的{​​{1}}类型的引用。

    在任何一种情况下,原始对象b都保持不变。您可以在转换后将其丢弃,但这是一个不同的问题,仅转换不会改变b

    (除非在极少数情况下,转换运算符或所涉及的构造函数对b采用非const引用并更改参数。但是,即使在这种情况下,它也不是您提到的vtable替换。)

    一旦创建了一个对象,它的类型在整个生命周期内都不会改变(直到它被销毁)。这意味着,你提到的vtable替换永远不会发生。

  2. 是的, C ++中的多态性是通过指针或引用来实现的。

    请注意,在您的示例中,a1和a2是从a和b复制的,转换不会以任何方式改变a或b。

  3. 是的,你是对的。或者任何类型的智能指针都可以接受(或者更可取)。

答案 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,因此a1a2ab完全不同。 }。