C ++中的多态性在引用时无法正常工作

时间:2013-08-24 05:16:32

标签: c++ pointers reference polymorphism

我有一个简单的代码,无法正确使用引用(多态)。

#include <iostream>
#include <string>

class Base {
public:
    Base() {}
    virtual ~Base() {}
    virtual std::string text() const {
        return "Base";
    }
};

class Derived: public Base {
public:
    Derived(Base& _b): b(_b) {}
    virtual ~Derived() {}
    virtual std::string text() const {
        return b.text() + " - Derived";
    }

private:
    Base& b;
};

int main(int argc, char const *argv[])
{
    Base b;
    Derived d1(b);
    std::cout << d1.text() << std::endl;

    Derived d2(d1);
    std::cout << d2.text() << std::endl;
    return 0;
}

输出:

Base - Derived
Base - Derived

我期望的输出中的第二行:Base - Derived - Derived。我读了一些资源和多态与引用和指针完美配合,但在这种情况下,它没有。如果我用指针替换引用,它会再次工作。那么,有人可以给我一些解释吗?

非常感谢!

4 个答案:

答案 0 :(得分:7)

您正在调用Derived的默认复制构造函数。因此,完成后d2将成为d1的简单成员副本,并且他们的b成员将引用相同的Base个实例。

要证明这一点,请将其添加到Derived班级

class Derived: public Base {
public:
    Derived(Derived& d) : b(d) {}
    Derived(Base& _b): b(_b) {}
    virtual ~Derived() {}
    virtual std::string text() const {
        return b.text() + " - Derived";
    }

private:
    Base& b;
};

这样你的输出就会变成:

Base - Derived
Base - Derived - Derived

请注意,这不是一个伟大的想法或多态性的恒星学习例子。 (但它是建筑覆盖的一个有趣的例子)。另请注意,这不是默认复制构造的典型覆盖(其中参数是const-ref-type)。因此,这不是最好的样本的部分原因。

答案 1 :(得分:1)

您的d1d2都有Derived类型,因此这是正常的。通常,参考文献是相反的; e.g。

Base b;
Derived d;
Base &dr = d;

std::cout << b.text() << std::endl;
std::cout << dr.text() << std::endl;

此处text()通过Base类型调用,但后者将调用Derived中的版本。

请注意,允许通过基类初始化派生类通常没有意义。假设您添加的类型Derived2具有与Derived大不相同的能力或状态。这个构造函数允许

Derived2 d2;
Derived d1(d2);

这可能是一个非常糟糕的主意。

答案 2 :(得分:1)

如果您检测代码,当您致电Derived d2(d1) Derived :: Derived(Base&amp;)时,您会看到 构造函数未被调用。这是因为d1参数更适合 隐式复制构造函数,它只是将b成员从d1复制到d2。

为了查看您期望的行为,您可以明确地将d1强制转换为(Base&)d1。如果你这样做 所以你将获得如下代码(使用仪器):

#include <iostream>
#include <string>

class Base {
public:
    Base() {}
    virtual ~Base() {}
    virtual std::string text() const {
        return "Base";
    }
};

class Derived: public Base {
public:
    Derived(Base& _b): b(_b) {std::cout << "init'ed with: " << _b.text() << std::endl;}
    virtual ~Derived() {}
    virtual std::string text() const {
        return b.text() + " - Derived";
    }

private:
    Base& b;
};

int main(int argc, char const *argv[])
{

    std::cout << "Creating Base" << std::endl;
    Base b;

    std::cout << "Creating d1" << std::endl;
    Derived d1(b);
    std::cout << d1.text() << std::endl;

    std::cout << "Creating d2" << std::endl;
    Derived d2(d1);
    std::cout << d2.text() << std::endl;

    std::cout << "Creating d3" << std::endl;
    Derived d3((Base&)d1);
    std::cout << d3.text() << std::endl;

    return 0;
}

这给出了预期的输出:

Creating Base
Creating d1
init'ed with: Base
Base - Derived
Creating d2
Base - Derived
Creating d3
init'ed with: Base - Derived
Base - Derived - Derived

答案 3 :(得分:0)

正如评论中正确指出的那样,现在它正在使用默认的复制构造函数,这就是你观察两者相同输出的原因。因此,d1只是复制到d2而不是用于d2中的基本成员变量。