如何延长局部变量的生命周期或使用引用的正确方法是什么
How to extend lifetime of the local variable or what is right way to use references
我正在开发一些 class 并遇到了这个问题。
考虑一下我有以下 class:
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;
}
};
然后是 class,其中 A
作为 属性:
class B {
public:
B(){}
A myList;
const A& getList() { return myList; };
};
这个函数在不同的情况下检查一些变量值和 returns 不同的对象:
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;
这种情况的目的是:
如果参数为 true
,函数 foo
应该 return(或移动)而不复制在 p
中更改的某些本地对象,或者应该 return全局变量 temp
的 属性 不调用 A
的复制构造函数(即 myList
的 return 引用),也不应该从 myList
中获取 myList
B
(它不应该从 B
中破坏 myList
,因此不能使用 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
如果你能把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();
}
}
输出是
ctor A
ctor A
125
------
1
dtor A
dtor A
你的函数 foo return 是 A 的实例,不是引用(也不是指针),所以你不复制或移动就无法访问 B.myList 内容。
为了获得此访问权限,您应该使用智能指针(如 Jarod42 所写)或像这样的简单指针:
B temp;
A* foo(bool f)
{
if (f)
{
A* ptr = new A;
*ptr->p = 125;
return ptr;
}
else
{
return &temp.getList();
}
}
但是这个特定的代码将不起作用因为 .getList() returns const 引用但是 foo returns 非常量指针(这可以但不应该被 const_cast<> 脏黑)。
通常您需要选择 foo 函数应该 return:
- A 的新实例class 具有特定数据
- 访问现有实例
如果您必须在运行时做出这个决定(例如通过您的 bool 参数),那么指针(简单或智能 - 无论如何)是唯一的选择(还记得删除手动分配的内存)。
最简单的解决方案可能是使用 Jarod42 的回答中的 std::shared_ptr
。但是如果你想避免使用智能指针,或者如果你不能更改 B
,你可以创建自己的包装器 class,它可能拥有也可能不拥有 A
。 std::optional
可能很方便:
class 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; }
};
如果需要,class 包含一个 optional
来拥有 A
,您可以使用移动语义将其移入。class 还包含一个引用(或指针)引用包含的值或引用另一个对象。
您可以 return 来自 foo
:
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
,则可以使用 boost::optional
,或者您可以使用 std::unique_ptr
,但要付出动态内存分配的代价。
我正在开发一些 class 并遇到了这个问题。 考虑一下我有以下 class:
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;
}
};
然后是 class,其中 A
作为 属性:
class B {
public:
B(){}
A myList;
const A& getList() { return myList; };
};
这个函数在不同的情况下检查一些变量值和 returns 不同的对象:
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;
这种情况的目的是:
如果参数为 true
,函数 foo
应该 return(或移动)而不复制在 p
中更改的某些本地对象,或者应该 return全局变量 temp
的 属性 不调用 A
的复制构造函数(即 myList
的 return 引用),也不应该从 myList
中获取 myList
B
(它不应该从 B
中破坏 myList
,因此不能使用 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
如果你能把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();
}
}
输出是
ctor A
ctor A
125
------
1
dtor A
dtor A
你的函数 foo return 是 A 的实例,不是引用(也不是指针),所以你不复制或移动就无法访问 B.myList 内容。
为了获得此访问权限,您应该使用智能指针(如 Jarod42 所写)或像这样的简单指针:
B temp;
A* foo(bool f)
{
if (f)
{
A* ptr = new A;
*ptr->p = 125;
return ptr;
}
else
{
return &temp.getList();
}
}
但是这个特定的代码将不起作用因为 .getList() returns const 引用但是 foo returns 非常量指针(这可以但不应该被 const_cast<> 脏黑)。
通常您需要选择 foo 函数应该 return:
- A 的新实例class 具有特定数据
- 访问现有实例
如果您必须在运行时做出这个决定(例如通过您的 bool 参数),那么指针(简单或智能 - 无论如何)是唯一的选择(还记得删除手动分配的内存)。
最简单的解决方案可能是使用 Jarod42 的回答中的 std::shared_ptr
。但是如果你想避免使用智能指针,或者如果你不能更改 B
,你可以创建自己的包装器 class,它可能拥有也可能不拥有 A
。 std::optional
可能很方便:
class 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; }
};
如果需要,class 包含一个 optional
来拥有 A
,您可以使用移动语义将其移入。class 还包含一个引用(或指针)引用包含的值或引用另一个对象。
您可以 return 来自 foo
:
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
,则可以使用 boost::optional
,或者您可以使用 std::unique_ptr
,但要付出动态内存分配的代价。