返回对多态对象的引用

Returning reference to polymorphic object

提问人:Raff89 提问时间:10/2/2023 更新时间:10/2/2023 访问量:54

问:

我读到对对象的引用保留了多态行为,考虑到这一点,我开始根据函数调用和各种回调中传递的 const 引用构建一个事件框架,它工作得很好,直到我需要从函数中获取事件作为返回。请考虑以下代码:

#include <iostream>
#include <memory>

class Base
{
public:
  inline Base(int type) : m_type(type){}
  virtual ~Base(){};
  virtual int method() const = 0;
  int m_type;
};

class Derived1 : public Base
{
public:
  inline Derived1(const int data) 
    : Base(1)
    , m_data(data){}
  virtual inline int method() const override {return m_data;} 

  int m_data;
};

// class Derived2 ...
// class Derived3 ...
// class Derived4 ...

const Base* ptr_factory()
{
  const Derived1* product = new Derived1(5);
  return product;
}

const Base& ref_factory()
{
  const Derived1 product(5);
  return std::move(product);
}

void do_the_thing(const Base& data)
{
  // if m_type is 1
  const Derived1& data_cast = dynamic_cast<const Derived1&>(data);
  
  std::cout<<"Hello World " << data.method();
}

int main()
{
  // pointer
  std::unique_ptr<const Base> ptr;
  ptr.reset(ptr_factory());
  do_the_thing(*ptr);
  return 0;
 
 // reference   
 // cannot create Base object here, because abstract, and cannot create Derived because I do not yet know which one
 const Base& ref = ref_factory(); <- undefined behavior and lots of sadness
 do_the_thing(ref);
 
 return 0;
}

基于引用的ref_factory会产生未定义的行为,我知道,std::move实际上并没有移动任何东西,只是给了我一个对已经被摧毁的本地对象的引用(和虚假的希望)。 我的问题是:有没有办法在给定的场景中避免指针? 我甚至无法在 main 中创建一个空的、默认构造的对象,因为基类是抽象的(即使不是,派生的数据也可能在以后被切片)。我也无法在 main 中创建 Derived 对象,因为工厂本身只知道它最终会是什么实际类型。 在你问我对指针有什么好处之前 - 我没有,但只要看看代码在基于引用的部分上看起来有多干净。 理想的解决方案是让ref_factory创建对象并将对象的所有权移至 main。 这在 C++14 中是否可能,或者我是否坚持必须在框架的这一部分中使用指针和新功能?

C++ 多态性 按引用传递

评论

2赞 n. m. could be an AI 10/2/2023
不能返回对自动对象的引用,无论是否多态。这样的引用是悬而未决的,任何使用它都是UB。 对你一点帮助都没有。std::move
1赞 HolyBlackCat 10/2/2023
通常你会回来.我看不出引用如何比这更清晰,除了键入该字符短 1 个字符之外。unique_ptr<Base>.->
1赞 Jarod42 10/2/2023
你想要那些丑陋的东西吗:?const Base& ref_factory() { static std::vector<std::uniqie_ptr<Base>> v; v.emplace_back(std::make_unique<Derived>(); *return v.back();}
0赞 Raff89 10/3/2023
@Jarod42观点。
0赞 Raff89 10/3/2023
@HolyBlackCat 我想当我让所有框架都运行在传递引用上时,这只是我的强迫症,但对于这个特定的位,我必须使用指针。我想,我可以在其他任何地方切换到指针,但是......我们不要得意忘形:)

答:

2赞 Useless 10/2/2023 #1

我的问题是:有没有办法在给定的场景中避免指针?

当然,您可以返回一个值类型对象,该对象内部包含指向动态对象的指针(或引用)。如果它除了管理所包含的动态对象的生存期之外什么都不,那么除了 though 之外,它没有任何实际意义。unique_ptr<Base>

理想的解决方案是让ref_factory创建对象并将对象的所有权移至 main。

核心准则普遍倾向于用于表示(和转让)所有权(R.20-21),并且还建议原始指针和引用都不应拥有(R3和R4)。unique_ptr

你绝对可以编写一个新的 RAII 值对象,其中包含对你的动态类型的拥有引用,但要使一些不那么标准和更令人惊讶的东西会是额外的工作。

这在 C++14 中是否可能,或者我是否坚持必须使用指针和新

无论如何,您都不应该使用原始指针和表达式。请改用 and。唯一指针本身可以很好地移动,如果您甚至不想 中看到 ptr,您可以给它一个 或 type 别名。newstd::unique_ptr<Base>std::make_unique<Base>(...)typedefusingunique_ptr

评论

0赞 Raff89 10/3/2023
感谢您的详尽回复。我确信(虽然有点不情愿)unique_ptr这是正确的方式。由于指针和引用之间有很多相似之处,我觉得后者准备完全取代前者来移动对象,同时避免复制。我想我想吃一个蛋糕(自动生命周期管理)并吃掉我(不必使用带有垃圾收集器的语言,而且它是开销的)
1赞 Useless 10/3/2023
您绝对可以在不使用垃圾回收器的情况下进行自动生命周期管理,无论是使用简单的自动局部变量,还是像这里一样的 RAII 对象。由于您还需要运行时多态性,因此简单的局部变量就不存在了。只是你的两个需求——所有权和动态分配——总是用指针来表达。