3

我有一个简单的代码,它不能与引用(多态性)一起正常工作。

#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

4 回答 4

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 类型)。因此,这不是最大样本的部分原因。

于 2013-08-24T05:46:32.383 回答
1

d1d2两者都有类型Derived,因此可以正常工作。通常引用是相反的;例如

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);

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

于 2013-08-24T05:43:28.413 回答
1

如果您检测代码,您将看到当您调用Derived d2(d1)Derived::Derived(Base&) 构造函数时没有被调用。这是因为 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
于 2013-08-24T05:44:52.450 回答
0

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

于 2013-08-24T05:40:40.313 回答