我有以下异常类。
class ExceptionTest : std::exception
{
public:
ExceptionTest(int value):
m_value(value)
{
}
~ExceptionTest()
{
}
private:
ExceptionTest(const ExceptionTest& test)
{
}
int m_value;
};
然后我以这种方式使用它 -
int checkexception()
{
throw ExceptionTest(2);
}
int main()
{
try
{
checkexception();
}
catch (ExceptionTest& exception)
{
cout<<"haha";
}
return 1;
}
即使复制构造函数是私有的,这也完全正常。
如果按值捕获异常,则失败 -
int main()
{
try
{
checkexception();
}
catch (ExceptionTest exception) --> this fails
{
cout<<"haha";
}
return 1;
}
我得到的错误是
error C2316: 'ExceptionTest' : cannot be caught as the destructor and/or copy
constructor are inaccessible
如果我没有在类
中定义复制构造函数,我会收到链接器错误class ExceptionTest : std::exception
{
public:
ExceptionTest(int value):
m_value(value)
{
}
~ExceptionTest()
{
}
private:
ExceptionTest(const ExceptionTest& test);
int m_value;
};
LINK:C:\ Users \ sumitha \ Documents \ Visual Studio 2010 \ Projects \ test \ Debug \ test.exe未找到或未由最后一个增量链接构建;执行完整链接 1&gt; main.obj:错误LNK2001:未解析的外部符号“private:__thiscall ExceptionTest :: ExceptionTest(class ExceptionTest const&amp;)”(?? 0ExceptionTest @@ AAE @ ABV0 @@ Z) 1&gt; C:\ Users \ sumitha \ Documents \ Visual Studio 2010 \ Projects \ test \ Debug \ test.exe:致命错误LNK1120:1个未解析的外部 ==========构建:0成功,1个失败,0个最新,0个跳过==========
如果上述情况属实,我们总是可以将异常类的复制构造函数设为private,这样调用者就会被强制通过引用捕获异常。我认为这是因为“返回值优化”
答案 0 :(得分:6)
这似乎是VS2010的C ++实现中的一个错误。
当throw
表达式时,通过复制(或移动)throw表达式的操作数来创建临时异常对象。如果表达式具有类类型,则涉及复制(或移动)构造函数,并且构造函数必须在throw
处可访问。如果异常对象的拷贝构造函数是私有的,那么该对象只能从成员函数或友元函数抛出。
此问题完全取决于异常对象是否随后被值捕获或稍后在程序中引用。
在构造临时异常对象时,可能会省略实际副本,但C ++要求仍然可以访问已使用的构造函数。
ISO / IEC 14882:2003 15.1 [except.throw] / 5:
如果可以在不改变程序含义的情况下消除临时对象的使用,除了执行与使用临时对象(12.2)相关的构造函数和析构函数,那么处理程序中的异常可以直接初始化使用throw表达式的参数。当抛出的对象是类对象,并且无法访问用于初始化临时副本的复制构造函数时,程序格式错误(即使临时对象可能被消除)。 同样,如果该对象的析构函数不可访问,则程序格式错误(即使临时对象可能被删除)。
此要求尚未在C ++ 0x中删除,尽管现在可以移动throw表达式而不是在适当的位置复制。
草案n3291 15.1 [except.throw] / 5:
当抛出的对象是类对象时,即使复制/移动操作被省略,也应该可以访问复制/移动构造函数和析构函数(12.8)。
答案 1 :(得分:1)
像马里诺说的那样,如果你通过引用捕获,就不会涉及复制结构。
因此,编译器不会查找它,并且永远不会在私有构造函数上失败:)
作为旁注,我记得特别针对异常,在规范中有关于传值语义的“异常”(没有双关语意)。我不知道具体细节,但它与
有关
....
catch (std::runtime_error e)
{
// ....
throw;
}
答案 2 :(得分:0)
当您使用引用(&amp; symbol)捕获异常时,您将获得生成的相同异常对象,即catch块中的变量将指向被抛出的内存中的同一异常对象,因此不需要复制缺点。如果您使用另一个按值捕获异常的catch块,那么编译器需要生成代码以创建正被捕获的异常对象的副本,并将此新副本分配给catch块中的变量,因此你需要有公共副本。
答案 3 :(得分:0)
将复制构造函数(和赋值运算符)设为私有是C ++中一种非常广泛使用的技术,用于防止复制。可以说,绝大多数C ++类(但不是异常,必须具有可访问的拷贝构造函数)应该禁用复制 - 我知道我自己的代码中的几乎所有类都这样做。