指针和堆栈分配对象的引用

时间:2014-03-14 14:10:21

标签: c++ c++11

假设我有以下代码(它可能过于简单,但我认为它会被采用):

struct BigObject {
   A a;
   B b;
};

struct A { ... };

struct B {
    A const* a_ptr;
    MyOtherClass someMethodUsingA() { ... }
}

class BigObjectBuilder {
    BigObject build() {
        BigObject o;
        o.a = buildA();
        o.b.a_ptr = &(o.a);

        return o;
    }
}

问题在于&(o.a)可能在以后指向任何内容,因为o的地址在返回时可能已经改变(调用移动或复制构造函数)。现在我发现大多数时间&(o.a)是相同的,因此调用*(o.b.a_ptr)不会导致段错误。 (我认为这是由于RVO,因为o不会被移动)。无论如何,只要我不确定o的地址没有改变,这段代码就不正确。

一个明显的解决方案是要求动态分配我的BigObject:

auto o_ptr = make_unique<BigObject>();

这个解决方案并不太糟糕(没有泄漏,并且解决了以前的问题。)但是我仍然觉得它不优雅:我不需要动态分配,我只需要为BigObject设置一个固定的地址。这将是我认为的Java做事方式。 另一个解决方案是在B中使用副本,但是o.a中的所有更改都不会影响o.b.a,我也不想要这样做。

第三种解决方案是在B中没有A的副本,ptr或引用,并在方法B :: someMethodUsingA()的参数中传递A.但是,再次调用此函数时,找到要传递的参数可能会很繁琐。有时候,B级指向A级的感觉更自然。

现在我发现这个问题在我的代码中一次又一次地发生:我想构建一个复杂的对象,子对象相互引用。根本问题是对象没有被创建和#34;它可能会在构建过程中稍后移动(但不是在构建完所有内容之后)。

他们的任何已知模式都适用于此吗?我想这是一个非常普遍的问题,如果不是,那一定是因为我没有以正确的方式构建我的系统......

有没有办法确保某种RVO?我的意思是,由标准确保,而不是由编译器确保。类似于编译器警告说&#34; BigObject&#34;无法移动到不同的地址&#34;

我还想过删除移动和复制ctor和赋值运算符。但是后来我无法按值返回一个对象,即使实际上没有任何动作......

欢迎任何有用的作品!

2 个答案:

答案 0 :(得分:1)

我无法理解为什么在struct BigObject中需要一个struct对象及其指针。 您可以像这样设计结构:

struct A { ... };
struct BigObject {
   A a;
   MyOtherClass someMethodUsingA() { /* Here you can use the variable a. */ }
};

如果您仍想按照自己的方式进行编码,可以为struct BigObject添加copy-construct和assign-construct函数,如下所示:

struct BigObject 
{
     A a;
     B b;

     // copy-construct function
     BigObject(const BigObject& old) 
     {
         a = old.a;
         b = old.b;
         b.a_ptr = &a; // pay attetion
     }  

     // assign construct function
     BigObject& operator=(const BigObject& old)
     {
          if (this == &old) // avoid self-assignment
          {
              return *this;
          }

          a = old.a;
          b = old.b;
          b.a_ptr = &a; 

          return *this;
     }
};

答案 1 :(得分:0)

在B函数中添加A引用参数,当BigObject调用函数时,它可以传递给它A. A和B应该是私有的。如有必要,将包装函数添加到BigObject,重定向到A和B的调用,根据需要传递A参数。

不要编写需要RVO才能工作的代码。