方法GetValues()
中使用的互斥锁是否在之前发布,之后复制构建dummy
实例?
class Protect
{};
class Test
{
public:
Protect GetValues() const;
private:
Protect m_protVal;
Mutex m_mutex;
};
Protect Test::GetValues() const
{
CLockGuard lock(m_mutex);
return m_protVal;
}
int main(int argc, char** argv)
{
Test myTestInstance;
Protect dummy = myTestInstance.GetValues();
}
我们假设CLockGuard
和Mutex
是boost lib提供的标准类。
答案 0 :(得分:12)
是:-)。正式地,返回时有两个“副本” value:用于实际返回值的一个特殊位置,和 返回后的第二个,到最后值必须到达的地方 放置。然而,可以优化其中之一或两者。破坏 局部变量发生在第一个之后,但在第二个之前。 (NRVO 和RVO可能导致第一个被优化,但它们不会影响 你的代码,因为你没有返回一个局部变量。)
答案 1 :(得分:2)
从Visual C ++开始,使用MSVC 9.0,以下代码
int add(int x, int y)
{
int z;
z = x + y;
return z;
}
int _tmain(int argc, _TCHAR* argv[])
{
add(10, 20);
return 0;
}
导致汇编
int add(int x, int y)
{
013613A0 push ebp //save the frame pointer
013613A1 mov ebp,esp
013613A3 sub esp,0CCh
013613A9 push ebx
013613AA push esi
013613AB push edi
013613AC lea edi,[ebp-0CCh]
013613B2 mov ecx,33h
013613B7 mov eax,0CCCCCCCCh
013613BC rep stos dword ptr es:[edi]
int z;
z = x + y;
013613BE mov eax,dword ptr [x] //load x
013613C1 add eax,dword ptr [y] //add y to x
013613C4 mov dword ptr [z],eax //store the result to z
return z;
013613C7 mov eax,dword ptr [z] //store the return value in eax
}
013613CA pop edi //unwind the stack
013613CB pop esi
013613CC pop ebx
013613CD mov esp,ebp
013613CF pop ebp
013613D0 ret
int _tmain(int argc, _TCHAR* argv[])
{
013613E0 push ebp
013613E1 mov ebp,esp
013613E3 sub esp,0C0h
013613E9 push ebx
013613EA push esi
013613EB push edi
013613EC lea edi,[ebp-0C0h]
013613F2 mov ecx,30h
013613F7 mov eax,0CCCCCCCCh
013613FC rep stos dword ptr es:[edi]
add(10, 20);
013613FE push 14h
01361400 push 0Ah
01361402 call add (136109Bh)
01361407 add esp,8
return 0;
0136140A xor eax,eax //store 0 to eax, the return value holder
}
0136140C pop edi //unwind the stack
0136140D pop esi
0136140E pop ebx
这让我说首先存储返回值,然后发生堆栈展开!
答案 2 :(得分:2)
就我所知,标准并不是特别明确,但这是我设法聚集的内容:
当阻止它们时,自动存储持续时间对象按6.7销毁 在出口宣布。 - 3.7.2
从范围退出时,将为所有自动存储调用析构函数(12.4) 在该范围内声明的持续时间(3.7.2)(命名对象和临时对象), 按其声明的相反顺序。 - 6.6
表达式为非void类型的return语句只能在函数中使用 返回一个值;表达式的值返回给调用者 功能。表达式隐式转换为函数的返回类型 它出现在哪里。退货声明可能涉及构建和复制 临时对象(12.2)。 - 6.6.3
即使避免了临时对象的创建(12.6),也都是语义 必须遵守限制,就像创建临时对象一样。 - 12.2
这似乎总体上证实了James所说的:在return m_protVal;
上创建了一个临时文件,然后按照其声明的相反顺序调用所有必须销毁的对象的析构函数(在这种情况下,只有析构函数) lock
被称为)。但是,我不完全确定如何解释以下内容:
临时对象作为评估完整表达式的最后一步被销毁(1.9) (词汇上)包含创建它们的点。 - 12.2
完整表达式被定义为“作为不是另一个表达式的子表达式的表达式”。我不知道return m_protVal
的哪一部分是完整的表达式:geordi说这是一个
decl'ion-seq→decl'ion→func-def→func-body→compound-stmt→stmt-seq→stmt→jump-stmt
而i
本身就是
decl'ion-seq→decl'ion→func-def→func-body→compound-stmt→stmt-seq→stmt→ jump-stmt→...→id-expr→unqual-id→ident
这使得不清楚是否可以在调用其余析构函数之前复制和销毁临时文件:我会说它可能不会,因为return m_protVal;
会导致块的结束,但是我在标准中找不到可以证实这一点的任何内容。
(另一方面,我没有看到任何这种行为会导致任何破坏的情况;没有人应该有一个指向临时的指针,所以它首先被销毁不是问题,如果临时有一个指针一个局部变量,当临时被销毁之后问题就出现了 - 不管在任何情况下编写这样的代码都是个好主意。)
答案 3 :(得分:1)
这是一个小型的完整测试程序(除了James Kanzes的良好解释),它将显示解锁是否在之前 之后 完成堆栈展开:
#include <iostream>
class PseudoLockGuard
{
public:
enum LockState { IsStillLocked, IsUnlocked};
PseudoLockGuard(LockState& value) : m_value(value) {};
~PseudoLockGuard() { m_value = IsUnlocked; };
private:
LockState& m_value;
};
PseudoLockGuard::LockState Test()
{
PseudoLockGuard::LockState indicator = PseudoLockGuard::IsStillLocked;
PseudoLockGuard lock(indicator);
return indicator;// Will return IsStillLocked or IsUnlocked?
}
int main(int , char** )
{
PseudoLockGuard::LockState result = Test();
std::cout << (result == PseudoLockGuard::IsStillLocked
? "Return Value before Unlock"
: "Return Value after Unlock");
// Outputs "Return Value before Unlock"
return 0;
}