简体   繁体   English

在C ++类的构造函数中抛出异常是一种好习惯吗?

[英]Is it good practice to throw an exception in the constructor of a C++ class?

I've got this constructor which throws an exception 我有这个构造函数抛出异常

GenericSocket::GenericSocket(const string& hostname, 
                             const string& servname):
                             _hostname(hostname),
                             _servname(servname)
{  
    initHints();
    int rv;
    if((rv = getaddrinfo(_hostname.c_str(), 
                    _servname.c_str(), 
                    &_hints, 
                    &_servinfo)) != 0) {  
        throw GenericSocketException();
    }  

} 

initHints() does a memset of _hints and sets some variables. initHints()执行_hints的memset并设置一些变量。

I test it with the google test framework like this: 我用google测试框架测试它,如下所示:

TEST(CreateObject2, getaddrinfoException)
{
    mgs_addrinfo_return = 1; 
    ASSERT_THROW(new GenericSocket("testhost", "4242"), GenericSocketException);
}

The test fails with a core dump: 测试因核心转储失败:

[ RUN      ] CreateObject2.getaddrinfoException
socket creation failed
terminate called after throwing an instance of 'common::GenericSocketException'
  what():  Socket creation failed
[1]    43360 abort (core dumped)  ./bin/test_common

Besides the fact that I dont know exactly what goes wrong, I suspect some uninitialised object gets deleted(?), a lot seems to happen under the hood, so I started to wonder if it is good practice to throw an exception in a constructor. 除了我不知道到底出了什么问题的事实,我怀疑一些未初始化的对象被删除(?),很多似乎发生在幕后,所以我开始怀疑在构造函数中抛出异常是否是好习惯。 Is it maybe better to put this functionality in another function which I can call after the creation of the object, and handle the exception afterwards? 是否可以更好地将此功能放在另一个我可以在创建对象后调用的函数中,然后处理异常?

IMHO, throwing an exception in a constructor is the best way to handle a situation like this - do you really want a usable object if there is no socket? 恕我直言,在构造函数中抛出异常是处理这种情况的最佳方法 - 如果没有套接字,你真的想要一个可用的对象吗? it doesn't make sense to me. 这对我没有意义。 If it's failing to resolve that address, there's a reason for that and that is worthy of an exception (as long as you handle it correctly!) 如果它没有解决该地址,那么就有理由,这是值得的例外(只要你正确处理它!)

In your particular case, you should test the return value and make the exception more useful... (for example, HostNotFound - which I'd guess is the case here) 在您的特定情况下,您应该测试返回值并使异常更有用...(例如, HostNotFound - 我猜这是这里的情况)

Yes it is. 是的。 You actually have no choice: constructors have no return values. 你实际上别无选择:构造函数没有返回值。

But take care of exception safety. 但要注意异常安全。 See http://www.drdobbs.com/184403429 for instance, or google "strong exception guarantee". 例如,请参阅http://www.drdobbs.com/184403429 ,或谷歌“强异常保证”。 Indeed, an object whose constructor has thrown will not be destructed (it has never existed) and must be left in a state which doesn't leak resources. 实际上,构造函数抛出的对象不会被破坏(它从未存在过)并且必须处于不泄漏资源的状态。

Of course, the only reasonable thing to do when you can't construct an object is to throw exception, otherwise you would end up with some zombie objects. 当然,当你不能构造一个对象时,唯一合理的做法是抛出异常,否则你最终会得到一些僵尸对象。 And answering your other question, no, you can't destroy an object that wasn't created. 并回答你的另一个问题,不,你不能破坏一个没有创建的对象。

Yes, throwing an exception is the most direct way to report that the constructor encountered problems, since they do not return values. 是的,抛出异常是报告构造函数遇到问题的最直接方法,因为它们不返回值。

Destructors , on the other hand, should not throw exceptions, since they will be called in the stack unwinding phase of exception handling, and throwing another exception at this point would result in an abort. 另一方面, 析构函数 应该抛出异常,因为它们将在异常处理的堆栈展开阶段被调用,并且此时抛出另一个异常将导致中止。

There are already at least two discussions about this on SO. 在SO上已经有至少两个关于此的讨论。 On both throwing exceptions from constructors and two-phase initialization: 在从构造函数抛出异常和两阶段初始化时:

1) When is it right for a constructor to throw an exception? 1) 构造函数何时抛出异常是正确的?

2) Throwing exceptions from constructors 2) 从构造函数中抛出异常

If you learn at construction that you won't be able to create a valid object then throwing an exception is arguably your best option. 如果你在构造中学习你将无法创建有效的对象,那么抛出异常可能是你最好的选择。 The user of the class is thus forbidden from carrying on in case of error, unless they can handle it. 因此,除非他们能够处理错误,否则禁止该类的用户继续进行。 That's usually the correct behaviour of a program. 这通常是程序的正确行为。

function()
{
    // 1. acquire resources.
    // 2. perform action.
    // 3. release resources.
}

If you can't fulfil step one then the other steps are futile. 如果你不能完成第一步那么其他步骤是徒劳的。 That's what we use RAII for: we acquire all the resources we need first, somewhat analogous to the old C style of programming with respect to stack variables. 这就是我们使用RAII的原因:我们首先获得了我们需要的所有资源,有点类似于堆栈变量的旧C编程风格。 With RAII if any of the resources fail to acquire -- via an exception in the constructor -- then all previously acquired resources will release automatically and the second step will never be attempted. 使用RAII,如果任何资源无法获取 - 通过构造函数中的异常 - 那么所有先前获取的资源将自动释放,并且永远不会尝试第二步。 All done behind the scenes, automatically, but this assumes you throw an exception in the constructor if you can't create the object. 所有这些都是在幕后自动完成的,但是假设您无法创建对象,则会假定您在构造函数中抛出异常。 It also assumes the destructor will do the clean-up. 它还假设析构函数将进行清理。

struct Connection {
    Connection() {
        if( ! connect() ) throw ConnectionError();
    }
    ~Connection() {  // may not throw under any circumstances
        try { disconnect(); }
        catch( ... ) { }
    }
    void send(const std::string& message);
    // ...
private:
    bool connect();
    // ...
};

void post(const std::string& message)
{
    // step one is easy for the user:
    Connection c;
    // step two is the bulk of the work:
    c.send("HELO");
    // step three is automatically done for the user.
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM