【问题标题】:How to return Derived from a function as a reference to Base?如何从函数返回 Derived 作为对 Base 的引用?
【发布时间】:2022-01-21 09:40:39
【问题描述】:

我想实现一个函数,它返回对Base 的引用,它实际上包含Derived(类型是多态的)。以下(不正确)代码中的一些内容:

struct Base { virtual ~Base() {} };
struct Derived: Base { int x = 5;};

const Base& get() {
    Derived d = {};
    const Base& b = d;
    return b;
}

int main() {
    const Base& b = get();
    const auto* a = dynamic_cast<const A*>(&b);
}

显然,此代码会导致未定义的行为,因为 main 中的 b 是一个悬空引用。

所以,我认为主要障碍如下:

  • ~将Derived 传递为Base~,我们需要创建一个引用。函数不能返回对局部变量的引用,因为它将引用堆栈释放的内存。

这个问题有什么解决方法吗(比如用指针代替引用什么的)?

【问题讨论】:

  • 不清楚(对我来说)你需要做什么。如果你需要创建一个实例,我会检查工厂函数是如何实现的。如果你只是想要一个对现有实例的基引用,你可以在基类中定义一个const Base&amp; get() const noexcept { return *this; },尽管这并不是必需的,因为你知道你可以只写const Base&amp; b = d;
  • @MatG 我想从函数返回Derived,就好像它是Base。我知道的唯一方法是使用引用和指针,当我们离开函数的范围时,它们会失效。我想知道这是如何实现的
  • 所以它是一个工厂函数。我通常在函数返回的std::unique_ptr&lt;Base&gt; 中实例化堆(新)上的派生类,因为您需要分配内存的所有者。
  • @MatG 对不起,我是新手,不知道工厂是什么:(。你能提供一个简短的回答吗??
  • @MatG 好的,谢谢你的提示 :)

标签: c++ inheritance undefined-behavior dangling-pointer


【解决方案1】:

如果您必须使用 dynamic_cast,我建议您使用指针而不是引用。为了使 API 更清晰一点,我会在处理拥有指针时使用 unique_ptr 或 shared_ptr ,并且仅在您临时访问对象时使用原始指针,例如将它们作为参数传递给函数时(不应该声称所有权)。这将有助于在所有权转移 (unique_ptr) 或预期(可能)共享 (shared_ptr) 时进行通信。

要记住的另一件事是,并非所有构建都启用了 dynamic_cast 所需的 RTTI(运行时类型信息)。对象通常可以设计成您永远不必检索对象的确切类型。

但是,为了使您的代码能够编译,我提供了三个替代版本。

参考文献

struct Base { virtual ~Base() {} };
struct Derived : Base { int x = 5; };

Derived d = {};

const Base& get() {
    return d;
}

int main() {
    const Base& b = get();
    const Derived& a = *dynamic_cast<const Derived*>(&b); // This would be undefined behaviour if b wasn't of type Derived as dynamic_cast would return null
}

使用 unique_ptr

#include <memory>

struct Base { virtual ~Base() {} };
struct Derived : Base { int x = 5; };

std::unique_ptr<const Base> get() {
    return std::make_unique<const Derived>();
}

int main() {
    std::unique_ptr<const Base> b = get();
    const Derived* d = dynamic_cast<const Derived*>(b.get());
}

使用 shared_ptr

#include <memory>

struct Base { virtual ~Base() {} };
struct Derived : Base { int x = 5; };

std::shared_ptr<const Base> get() {
    return std::shared_ptr<const Derived>();
}

int main() {
    std::shared_ptr<const Base> b = get();
    std::shared_ptr<const Derived> d = std::dynamic_pointer_cast<const Derived>(b);
}

【讨论】:

  • 还值得一提的是,unique_ptr 通常是更通用的选择,因为调用者很容易将其转换为 shared_ptr,但反过来并不容易。
猜你喜欢
  • 2020-09-29
  • 2016-09-28
  • 1970-01-01
  • 2021-10-09
  • 1970-01-01
  • 2012-11-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多