C++ 构造函数中的异常处理&x2019;s初始值设定项列表
在我的项目中,我发现了一段代码,其中在构造函数的初始值设定项列表中调用了一个方法C++ 构造函数中的异常处理&x2019;s初始值设定项列表,c++,exception-handling,C++,Exception Handling,在我的项目中,我发现了一段代码,其中在构造函数的初始值设定项列表中调用了一个方法 Test2(Test* pTest):m_pTest(pTest), m_nDuplicateID(pTest->getTestID()) { } 我注意到Test2的用户可能会将NULL传递给构造函数。由于指针未经验证就被使用,因此存在访问冲突的可能性 这促使我研究构造函数的初始值设定项列表中的异常处理。我在一篇文章中发现,try可以在初始值设定项列表中使用。我编写了一个小测试程序来测试这
Test2(Test* pTest):m_pTest(pTest), m_nDuplicateID(pTest->getTestID())
{
}
我注意到Test2的用户可能会将NULL传递给构造函数。由于指针未经验证就被使用,因此存在访问冲突的可能性
这促使我研究构造函数的初始值设定项列表中的异常处理。我在一篇文章中发现,try可以在初始值设定项列表中使用。我编写了一个小测试程序来测试这个概念:
//Test class stores the unique ID and returns the same with API getTestID
class Test
{
public:
Test(int nID):m_nID(nID){
}
int getTestID() const
{
return m_nID;
}
private:
int m_nID;
};
class Test2
{
public:
Test2(Test* pTest)
try :m_pTest(pTest), m_nDuplicateID(pTest->getTestID())
{
}
catch (...)
{
cout<<"exception cought "<< endl;
}
void printDupID()
{
cout<<"Duplicate ID" << m_nDuplicateID << endl;
}
private:
Test* m_pTest;
int m_nDuplicateID;
};
int main(int argc, char* argv[])
{
Test* pTest = new Test(10);
Test2 aTest2(pTest);
aTest2.printDupID();
delete pTest;
return 0;
}
//测试类存储唯一的ID,并使用API getTestID返回相同的ID
课堂测试
{
公众:
测试(int nID):m_nID(nID){
}
int getTestID()常量
{
返回m_nID;
}
私人:
国际货币基金组织;
};
类Test2
{
公众:
测试2(测试*pTest)
try:m_-pTest(pTest),m_-nDuplicateID(pTest->getTestID())
{
}
捕获(…)
{
cout根据,在VC++6.0中似乎无法做到这一点
您必须要么将Uggad 7或只是在构造函数体中初始化,否则, 首先,如果您引用空指针,标准C++不保证会抛出异常,因此您的代码对于这种情况是无用的。
其次,如果抛出异常,您的异常处理程序会做什么
第三,构造函数/函数异常块被广泛认为是时间的象征——看看Herb Sutter的GotW站点上的这篇文章和其他文章。C++标准第15/3节
函数try块与
使用Tor初始值设定项,
如果存在,以及功能体
执行期间引发异常
中的初始值设定项表达式的
ctor初始值设定项或在
功能体的执行
将控制权转移到
函数try块的方式与
测试过程中引发的异常
执行try块传输
将控件复制到其他处理程序
人们还在使用VC6吗?
说真的,VC6不是一个标准投诉编译器。帮你自己一个忙,至少得到VS2005。VC6是你的问题。
试试VS2008 express,看看它是否可以编译
当然,另一个选项是引用需要绑定的构造。您不能只使用一个函数来检查ptr,例如:
template<typename P>
P* checkPtr (P* p)
{
if (p == 0)
throw std::runtime_error ("Null pointer");
return p;
}
class Test2
{
public:
Test2 (Test* pTest)
: m_pTest (checkPtr (pTest))
{
}
Test* m_pTest;
};
模板
P*checkPtr(P*P)
{
如果(p==0)
抛出std::runtime_错误(“空指针”);
返回p;
}
类Test2
{
公众:
测试2(测试*pTest)
:m_pTest(checkPtr(pTest))
{
}
测试*m_测试;
};
(适用于谷歌同行)
如果我们不想存储ptr/共享ptr的副本,另一种解决方案是:
class Foo::Pimpl
{
public:
bool paramTest_;
Pimpl(ConstNodePtr root)
try :
paramTest_( root ? true : throw std::invalid_argument("Foo (pimpl) constructed from NULL node")),
...
{
...
} catch (...)
{
throw; // rethrow
}
已经有很多有用的答案了,但我会尝试补充一点,也许它会帮助一些人
首先,正如其他已经提到的,取消代码< NulLPTR >代码>或无效指针(地址)不会在标准C++中引发异常。MSVC支持它,但它不是可移植的。请在./P>中阅读更多内容。
构造函数中的函数try block不允许您抑制异常,如果您不抛出另一个异常,它将传播。当您输入catch子句时,该类的所有成员都已被销毁。因此,您可以在其中做的唯一合理的事情是以某种方式记录错误,或者可能更改某些全局变量。这就是为什么它或多或少被认为是无用的
至于你的初始代码
Test2(Test* pTest):m_pTest(pTest), m_nDuplicateID(pTest->getTestID())
{
}
您可以使用三元运算符仅在初始值设定项列表中检查pTest的null值,并在其为null时执行一些适当的操作-只需将m_nDuplicateID设置为null ptr或其他一些值(取决于其类型),调用另一个函数并使用其返回类型,等等:
Test2(Test* pTest):
m_pTest(pTest),
m_nDuplicateID( pTest ? pTest->getTestID() : /*some value or call*/ )
{
}
您甚至可以使用几个嵌套的三元运算符来创建更复杂的执行流
为了完整起见,您的代码并非如此,但可能有人在同样的情况下使用了它。如果您使用类的成员m_pTest初始化m_duplicateId,这将取决于这些成员在类声明中的顺序,因为初始化器列表中的类成员是按照声明的顺序初始化的,而不是t按照它们在初始值设定项列表中出现的顺序,因此这可能是一个问题,最好避免成员初始化顺序依赖关系:
class A
{
A( B* pTest );
int m_nDuplicateID;
B* m_pTest;
};
A::A( B* pTest ) :
m_pTest( pTest ),
m_nDuplicateID( m_pTest->someMethod() ) // here m_pTest isn't initialized yet,
// so access violation probably
{
}
我会选择升级,因为初始化列表有时无法避免(例如初始化引用)在构造函数中可能会抛出异常。你不可能做到赫伯的文章:构造异常块只能用于重新引用不同的异常,或用于日志记录等副作用。出于这些目的,它们当然不会浪费时间。它们不能做更多。是的,不要使用构造函数异常只需检查输入参数,并适当地声明或投掷。“Eric P”的回答中的文章称它不符合标准。除了此之外,我想知道为什么VC 6不支持。从文章“你不能在Visual C++ 6中编译它,因为它不严格地确认C++标准。”VC++ 6在标准VC6之前就因为不符合标准C++而臭名昭著。不幸的是,在VC6中有很多百万线的单片C++应用程序,它将花费很多开发者年来移植到现代C++中,许多公司不想进行切换。n、 也就是说,成本的增长。维护这些古老的代码并不便宜,也不会变得更便宜。我同意,但你并不总能获得足够的影响力来进行更改(特别是作为一名承包商),而且通常还有许多更紧迫的问题需要处理。
class A
{
A( B* pTest );
int m_nDuplicateID;
B* m_pTest;
};
A::A( B* pTest ) :
m_pTest( pTest ),
m_nDuplicateID( m_pTest->someMethod() ) // here m_pTest isn't initialized yet,
// so access violation probably
{
}