如何延长局部变量的生命周期或使用引用的正确方法

时间:2017-07-21 08:45:43

标签: c++ c++11 reference move object-lifetime

我正在开发一些课程并且遇到了这个问题。 考虑我有以下课程:

struct A
{
    int *p;
    A() 
    {
        p = new int(1); 
        cout << "ctor A" << endl; 
    }
    A(const A& o) 
    { 
        cout << "copy A" << endl;
        p = new int(*(o.p));
    }
    A(A&& o) 
    {
        cout << "move A" << endl;
        p = std::move(o.p);
        o.p = NULL;
    }
    A& operator=(const A& other)
    {       
        if (p != NULL)
        {
            delete p;
        }
        p = new int(*other.p);
        cout << "copy= A" << endl;
        return *this;
    }
    A& operator=(A&& other)
    {       
        p = std::move(other.p);
        other.p = NULL;
        cout << "move= A" << endl;
        return *this;
    }
    ~A()
    {
        if(p!=NULL)
            delete p;
        p = NULL;
        cout << "dtor A" << endl;
    }
};

以下以A作为属性的类:

class B {
public:
  B(){}
  A myList;
  const A& getList() { return myList; };
};

这个函数检查一些变量值并在不同的情况下返回不同的对象:

B temp;
A foo(bool f)
{
    A a;
    *a.p = 125; 
    if (f)
        return a;
    else
    {
        return temp.getList();
    }
}

现在,我想使用这个函数:

A list1 = foo(true);
if(list1.p != NULL)
    cout << (*list1.p) << endl;

cout << "------"<<endl;
A list2 = foo(false);
if (list2.p != NULL)
    cout << (*list2.p) << endl;

这种情况的目的是:

如果参数为foo,函数p应该返回(或移动)而不复制true中的更改的某个本地对象,或者应返回全局变量temp的属性而不返回调用A的复制构造函数(即返回myList的引用)并且它不应该从myList抓取B(它不应该从{{myList中删除B 1}},如果参数为std::move,则无法使用false

我的问题是:

我应该如何更改功能foo以遵循上限条件? foo的当前实现在true情况下正常工作并移动该局部变量,但如果false它调用list2的复制构造函数。其他想法是以某种方式延长局部变量的生命周期,但添加const引用对我来说不起作用。目前的输出是:

ctor A
ctor A
move A
dtor A
125
------
ctor A
copy A
dtor A
1
dtor A
dtor A
dtor A

3 个答案:

答案 0 :(得分:3)

如果您可以将B更改为

class B {
public:
  B(){}
  std::shared_ptr<A> myList = std::make_shared<A>();
  const std::shared_ptr<A>& getList() const { return myList; };
};

然后foo可以是:

B b;
std::shared_ptr<A> foo(bool cond)
{
    if (cond) {
        auto a = std::make_shared<A>();
        *a->p = 125; 

        return a;
    } else {
        return b.getList();
    }
}

Demo

输出

ctor A
ctor A
125
------
1
dtor A
dtor A

答案 1 :(得分:2)

最简单的解决方案可能就是在Jarod42的答案中使用std::shared_ptr。但是如果你想避免使用智能指针,或者你不能改变B,你可以创建自己的包装类,它可能拥有A,也可能不拥有std::optionalclass AHolder { private: std::optional<A> aValue; const A& aRef; public: AHolder(const A& a) : aRef(a) {} AHolder(A&& a) : aValue(std::move(a)), aRef(aValue.value()) {} const A* operator->() const { return &aRef; } }; 可能非常方便:

optional

如果需要,该类包含A拥有foo,您可以使用move-semantics将其移入。该类还包含引用包含值的引用(或指针)或引用另一个对象。

您可以从AHolder foo(bool f) { A a; *a.p = 125; if (f) return a; else { return temp.getList(); } }

返回此内容
  auto list1 = foo(true);
  if(list1->p != nullptr)
    cout << (*list1->p) << endl;

  cout << "------"<<endl;
  auto list2 = foo(false);
  if (list2->p != nullptr)
    cout << *list2->p << endl;

调用者可以访问包含的引用:

std::optional

Live demo

如果您无法访问boost::optional std::unique_ptr,或者您可以以动态内存分配为代价使用h = 1/20 g2 = matrix(NA, 5000, 1) #notice na.rm = TRUE below which will remove NAs in the sum calculation for (j in 1:5000) { g2[j] = pexp(j + (1 / 2) * h, rate = 1, lower.tail = TRUE, log.p = FALSE) if (sum(g2, na.rm = TRUE) > 0.8) break print(g2) }

答案 2 :(得分:0)

您的函数 foo 返回 A 的实例,而不是引用(也不是指针),因此您无法访问 B.myList < / em>内容无需复制或移动。

为了获得此访问权限,您应该使用智能指针(如Jarod42写的)或只是这样的简单指针:

B temp;
A* foo(bool f)
{
    if (f)
    {
        A* ptr = new A;
        *ptr->p = 125;
        return ptr;
    }    
    else
    {
        return &temp.getList();
    }
}

然而,这个特定的代码将不起作用coz .getList()返回const引用,但 foo 返回非const指针(这可能但不应该是使用const_cast&lt的黑客攻击;&GT)。

通常,您需要选择 foo 函数应该返回的内容:

  • 具有特定数据的 A 类的新实例
  • 访问现有实例

如果你必须在运行时做出这个决定(例如你的bool参数)那么指针(简单或智能 - 无论如何)是唯一的选择(还记得删除手动分配的内存)。