多态C++引用

polymorphic C++ references

本文关键字:引用 C++ 多态      更新时间:2023-10-16

我想知道如何使用引用而不是指针进行多态性。

若要澄清,请参阅以下最小示例:

class A;
class B {
  public:
    A& a; ///////////////// <- #1
    B();
    void doStuff();
};
class A {
  public:
    virtual void doSmth() = 0;
};
void B::doStuff() {
  a.doSmth();
}
class A1 : public A {
  public:
    void doSmth() {
    }
};
B::B() : a(
    *        ////////////// <- #2
      (new A1)  /////////// <- #3
     ) {
}

这可以编译和工作,但这里最重要的一点是,第 #1 行中的a是一个引用,所以为了能够多态地使用它(这是一个实际的单词吗?(,如第 #3行所示,我必须通过取消引用来"将指针转换为引用"。

这让我感到有点奇怪,我想知道是否有更好的(在更清洁的意义上(方法。只有我一个人吗?

理由

如果我根本不需要new,那就太好了,但是在声明(! B 我不知道如何创建A1(!(的实例,因为A是一个前向声明 - A1在与B相同的编译单元中实现。不过,在这种情况下是否真的需要动态内存分配?你会怎么做?

对不起,这个稍微双重的问题。

编辑

注意:B很大(我不能为它制作模板类(,并且当程序终止时会超出范围 - a很小并且使两个大模块相互通信,只要B的实例存在(只有一个(,就需要它。

编辑 2

我刚刚意识到,由于AB都是有效的单例,我可以简单地在B的编译单元中创建A1static实例,避免动态内存分配(即使有两个B它们也可以轻松使用相同的A实例(。公平地说,我没有将此作为答案发布,但会接受促使我想出此解决方案的答案。

没什么奇怪的。多态性适用于指针引用:

struct Base { };
struct Derived : Base;
void foo(Base &);
int main() {
  Derived x;
  foo(x);    // fine
}

您将此与另一个问题混为一谈,即创建对动态对象的引用:

T * pt = new T;
T & rt = *pt;
T & x = *new T;  // same effect

请注意,通过引用跟踪动态对象通常是非常糟糕的样式,因为删除它的唯一方法是通过 delete &x; ,并且很难看到x需要清理。

您的设计有两种直接的替代方法:1( 使a成为 B 中的成员对象,或 2( 使a成为shared_ptr<A>unique_ptr<A>并将发起器更改为 a(new A1) 。这完全取决于您是否真的需要多态行为,即您是否有其他构造函数用于B,这些构造函数将不同的派生类分配给a而不是A1

这确实有点奇怪。 如果你想要一个类型为 A1 的成员变量(而不是引用(,为什么不重新排列你的代码,以便A1的定义出现在B的定义之前?

不过,这是否真的需要动态内存分配 箱?

不。只需先定义 A1,然后使其成为 B 的普通成员。

多态性在引用和指针上都很好用。

呃,这还不够吗?

#include <iostream>
struct A;
struct B
{
  B(A& a);
  void foo();
  A& _a;
};
struct A
{
  virtual void foo() =0;
};
struct A1 : public A
{
  virtual void foo() { std::cout << "A1::foo" << std::endl; }
};
B::B(A& a) : _a(a) {}
void B::foo() { _a.foo(); }

int main(void)
{ 
  A1 a;  // instance of A1
  B b(a); // construct B with it
  b.foo();
}

不过,在这种情况下是否真的需要动态内存分配?

动态内存分配或将引用注入 B 的 ctor。

难想象为什么引用可以像指针一样多态地工作(更不用说引用通常作为指针实现(。下面是一个快速示例:

class Base { 
public:
    virtual void something() { }
};
class Derived : public Base {
public:
    void something() { }
};
Base& foo() {
    static Derived d;
    return d;
}
foo().something(); // calls Derived's something

另外,为什么要为参考分配动态内存?在这种情况下,您可能根本不应该使用引用。此外,使用引用成员编写类可以有效地防止分配(正如我听到有人说的那样(。

我意识到这是一个非常古老的帖子,但您还有另一种选择来处理动态分配对象的引用。您可以分配对动态分配对象的引用。下面是一些虚拟代码,让您了解其工作原理。

struct A
{
  int b;
  virtual void print();
  A(int val):b(val) {}
};
struct A_child:public A
{
  A_child(int val):A(val) {}
  void print();
};
void A:print()
{
cout<<"parentn";
}
void A_child:print()
{
cout<<"childn";
}
struct test_ref
{
A *& ref;
test_ref(A * ptr) : ref(ptr)
}
int main()
{
  test_ref parent(new A(12));
  parent.ref->print();
  test_ref child(new A_child(15));
  child.ref->print();
} 

老实说,我不确定这什么时候是个好主意。我只是想展示一种替代方法,在初始化对象时不必取消引用动态分配的内存。

我也非常确定在初始化将指针存储为引用指针的类时动态分配指针可能会导致内存泄漏,除非您可以删除引用指针。