多态C ++引用

时间:2011-08-25 14:24:54

标签: c++ polymorphism reference

我想知道如何你可以用引用做多态,而不是指针。

澄清一下,请参阅以下最小例子:

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
     ) {
}

这可以编译并运行,但最重要的一点是,行a中的#1是一个引用,所以为了能够以多态方式使用它(这是一个真正的单词吗? ),如行#3所示,我必须通过解除引用来“将指针转换为引用”。

这让我有点奇怪,我想知道是否有更好的(在更清洁的意义上)方式。只是我吗?

原理

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

对于这个有点双重问题的人抱歉。

修改

注意:B是巨大的(我无法制作它的模板类),并且在程序终止时会精确地超出范围 - a很小并使两个大模块对话只要B的实例存在(只有一个),就会需要彼此。

编辑2

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

7 个答案:

答案 0 :(得分:32)

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

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}}需要清理。

您的设计有两种直接替代方案:1)在a中将B成为成员对象,或2)使a成为shared_ptr<A>或{{1}并将initalizer更改为unique_ptr<A>。这完全取决于您是否确实需要多态行为,即如果您有a(new A1)的其他构造函数,它将不同的派生类分配给B以外的a

答案 1 :(得分:3)

  

但是,在这方面是否真的需要动态内存分配   情况?

没有。首先定义A1,然后使其成为B的正常成员。

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

答案 2 :(得分:3)

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

答案 3 :(得分:1)

呃,这还不够吗?

#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();
}

答案 4 :(得分:0)

  

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

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

答案 5 :(得分:0)

无法想象为什么引用可以像指针一样多态地工作(更不用说引用通常也被实现为指针)。这是一个简单的例子:

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

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

答案 6 :(得分:-1)

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

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<<"parent\n";
}

void A_child:print()
{
cout<<"child\n";
}

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

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

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