用异常抛出构造函数初始化对象的正确方法
这似乎是一个微不足道的问题,但现在我挂了几个小时(可能太多Java杀死了我的C++ braincells)。用异常抛出构造函数初始化对象的正确方法
我创建了具有以下构造函数的类(即没有默认构造函数)
VACaptureSource::VACaptureSource(std::string inputType, std::string inputLocation) {
if(type == "" || location == "") {
throw std::invalid_argument("Empty type or location in VACaptureSource()");
}
type = inputType;
location = inputLocation;
// Open the given media source using the appropriate OpenCV function.
if(type.compare("image")) {
frame = cvLoadImage(location.c_str());
if(!frame) {
throw std::runtime_error("error opening file");
}
}
else {
throw std::invalid_argument("Unknown input type in VACaptureSource()");
}
}
当我想创建一个实例,我用
// Create input data object
try {
VACaptureSource input = VACaptureSource("image", "/home/cuneyt/workspace/testmedia/face_images/jhumpa_1.jpg");
}
catch(invalid_argument& ia) {
cerr << "FD Error: " << ia.what() << endl;
usage(argv[0]);
}
catch(runtime_error& re) {
cerr << "FD Error: " << re.what() << endl;
usage(argv[0]);
}
然而,在这种情况下,实例是本地块,我不能在其他地方引用它。另一方面,我不能说
VACAptureSource input;
在程序的开始,因为没有默认的构造函数。
这样做的正确方法是什么?
谢谢!
如何使用指针(或其某些RAII版本)?
VACaptureSource* input = NULL;
try {
input = new VACaptureSource(...);
} catch(...) {
//error handling
}
//And, of course, at the end of the program
delete input;
谢谢,我想过那个,但是认为C++中的指针“邪恶”,即仅用作最后的手段?除了使用指针之外没有别的办法吗? – recipriversexclusion 2009-06-30 15:37:53
“邪恶”是一个非常强大的词。但是,正如CAdaker所建议的那样,RAII(即auto-destructing)变体(例如std :: auto_ptr)或任何适当的Boost自动指针都是明智的选择。 – 2009-06-30 15:55:17
顺便说一下,你可能只想使用有效的实例,这意味着你可能想在完成需要完成的任务后重新从catch块中抛出。 – 2009-06-30 15:58:33
局部变量的作用域被分配给它的块(像Java),但只要块结束就会破坏(不像Java),所以你应该在try块本身做所有你想要的东西(如果你只想处理构造函数的例外,这可能不是所希望的),或者你应该把对象分配给别的地方(例如heap),并在父块中使用一个指针来访问它。
你可以使用一个指针
VACaptureSource* input;
// Create input data object
try {
input = new VACaptureSource("image", "/home/cuneyt/workspace/testmedia/face_images/jhumpa_1.jpg");
}
catch(invalid_argument& ia) {
cerr << "FD Error: " << ia.what() << endl;
usage(argv[0]);
}
catch(runtime_error& re) {
cerr << "FD Error: " << re.what() << endl;
usage(argv[0]);
}
而且你需要释放的对象,当你完成你为什么要提到它的try
外面使用它
delete input
我再次看不到指针的内容。 – 2009-06-30 17:53:00
块?
而不是
try {
VACaptureSource input = VACaptureSource("image", "/home/cuneyt/workspace/testmedia/face_images/jhumpa_1.jpg");
}
//catch....
//do stuff with input
你可以一切进入try块:
try {
VACaptureSource input = VACaptureSource("image", "/home/cuneyt/workspace/testmedia/face_images/jhumpa_1.jpg");
//do stuff with input
}
//catch....
,或者你可以因子它到一个单独的功能,这是从try块称为:
void doStuff(VACaptureSource& input){
//do stuff with input
}
try {
VACaptureSource input = VACaptureSource("image", "/home/cuneyt/workspace/testmedia/face_images/jhumpa_1.jpg");
doStuff(input);
}
//catch....
最后一个甚至为您提供了分离建筑和使用的好处,它很好地放在单元测试中,您可能希望函数在模拟对象上工作。
另一个很好的答案由你包括几种选择。我喜欢这些:)但为了公平并给出可能的原因,我还经常希望那些“尝试”块中的东西“泄漏”到封闭的范围中。但是这可能是危险的,请考虑以下代码:try {string a;/* 2009-06-30 17:16:01
如何添加一个默认的构造函数使对象处于特殊的未配置状态?然后有一个create()函数来实际创建它。
然后,你可以这样做:
VACaptureSource input;
try
{
input.create("image", "...");
}
catch(...)
{
...
}
根据这可能是比指针搞乱更好的情况。虽然那么你还必须检查创建()做某件事之前实际上叫...
我实际上没有看到这里的任何probelms:
一对夫妇的事情,我会更新:
- 通过const引用捕获异常。
- 编译器可能会优化掉代码中的复制构造
但是如果没有它,它看起来会更整洁。只需用参数声明输入即可。 - 我会重构构造函数以获取const引用参数
我会在初始化列表中初始化它们。 - 此外,我会确保成员'框架'实际上是一个智能指针。
所以我会这样做(为了清晰)。
VACaptureSource::VACaptureSource(std::string const& inputType,
std::string const& inputLocation)
:type(inputType)
,location(inputLocation)
{
// Other Code that throws.
}
void playWithCode()
{
// Get input information from user.
VACaptureSource input("image", "/home/cuneyt/workspace/testmedia/face_images/jhumpa_1.jpg");
// use the input object.
// Do not need to play with pointers here.
}
int main()
{
try
{
playWithCode();
}
catch(invalid_argument const& ia)
{ cerr << "FD Error: " << ia.what() << endl;
usage(argv[0]);
}
catch(runtime_error const& re)
{ cerr << "FD Error: " << re.what() << endl;
usage(argv[0]);
}
}
可我只是观察到几乎任何但最琐碎的构造可以预期抛出异常。因此,在某种意义上,你不应该认为异常是“特殊的”,而是编写你的代码,以便它自然地处理它们。这意味着使用RAII,以及其他在这里回答的其他技术已经提出。
简单。不要在构造函数中抛出异常。您不仅需要将构造函数包装在try块中,在发生异常时您将无法很好地处理内存(您是否调用析构函数?需要删除多少类的内存? )
UPDATE0:虽然,我不确定内存管理是否是一个问题,如果您使用的是实例。
UPDATE1:嗯,也许我在考虑析构函数的例外。
int
main2(int argc, char* argv[])
{
MyClass class;
class.doSomething();
}
int
main(int argc, char* argv[])
{
int result = 0;
try {
main2(argc, argv);
} catch (std::exception& se) {
// oh noes!
result = 1;
}
return result;
}
我不能在节目的开头说
VACaptureSource input;
因为没有默认构造函数。
有一个很好的理由,你没有创建一个默认构造函数:即VACaptureSource
只有在与文件关联时才有意义。所以不要创建一个默认的构造函数。而是简单地认识到VACaptureSource
对象的范围是try
块,并在其中使用它。
你会如何在Java中解决这个问题?应用相同的解决方案。这个问题不依赖于语言。不同的语言只为不同的语法提供了不同的语法来实现等效目的,考虑到当你在Java中定义一个变量时,你不是声明一个对象,而是一个_reference_(C++术语中的指针),所以在Java和C++中看起来类似的代码并不是真正等效的代码。 – 2009-06-30 22:15:23