(不)使用的std ::在异常字符串 [英] (not) using std::string in exceptions

查看:69
本文介绍了(不)使用的std ::在异常字符串的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在读,我不应该抛出的std ::字符串或一些其他类分配内存。如这里或更重要的此处点上3 - 不要嵌入的std ::字符串对象

I'm always reading that I should not to throw a std::string or some other classes allocating memory. like here or more importantly here on point 3. - Don't embed a std::string object.

所以现在我试图插入提高::例外来我的项目,我看到了什么:许多字符串的。

So now I'm trying to insert boost::exception to my project and what do I see: lots of strings.

为什么不提高履行自己的建议?

Why doesn't boost comply with its own recommendation?

如果我有不能硬codeD,就像在配置文件中的参数萨法德,我怎么可以把它们放进一个例外,而无需使用的std ::字符串

And if I have parameters which can't be hardcoded, like safed in an config-file, how can I put them into an exception, without using std::string?

或者是指引的不使用的std ::字符串 只有不要使用 STD: :字符串尽可能少指引?我有点迷茫......

Or is the guideline don't use std::string only a do use std::string as seldom as possible guideline? I'm a bit confused...

我做了一些研究。请纠正我,如果我错了。

I've done some research. Please correct me if i'm wrong.

如果我的理解是正确的,它是所有关于分配的 抛出,什么是发生在所分配的内存中。所以,如果我在构造函数中为它分配内存丢失,它不能在异常的析构函数,这将产生一个内存泄漏释放。但它的好扔之前分配这一点,所以异常是干净的。

If I understand it right, it's all about the allocation during the throw and what is happening to the allocated memory. So the memory gets lost if I allocate it in the constructor and it can't be freed in the destructor of the exception, that will produce a memory-leak. But it's okay to allocate this before throwing, so the exception is clean.

我试过这样:

I tried this:

struct xexception {
  int *ttt[10];
  xexception() {
    ttt[0] = new int[0xfffffffL];
    ttt[1] = new int[0xfffffffL];
    ttt[2] = new int[0xfffffffL];
    ttt[3] = new int[0xfffffffL];
    ttt[4] = new int[0xfffffffL];
    ttt[5] = new int[0xfffffffL];
    ttt[6] = new int[0xfffffffL];
    ttt[7] = new int[0xfffffffL];
    ttt[8] = new int[0xfffffffL];
    ttt[9] = new int[0xfffffffL];
  }

  ~xexception() throw() {
    //never happen
    delete[] ttt[0];
    delete[] ttt[1];
    delete[] ttt[2];
    delete[] ttt[3];
    delete[] ttt[4];
    delete[] ttt[5];
    delete[] ttt[6];
    delete[] ttt[7];
    delete[] ttt[8];
    delete[] ttt[9];
  }
};

int main(int argc, const char *argv[]) {
  try {
    throw(xexception());
  }
  catch (const xexception &e) {
    std::cerr << "\nttt " << e.ttt[0][0] << std::endl;
  }
  catch (std::bad_alloc) {
    std::cerr << "bad alloc" << std::endl;
  }

  return 0;
}

的结果是,我得到的 bad_alloc 和庞大的内存泄漏。

现在,如果我之前做的配置,它的的抛出bad_alloc,但之前创建的例外。

Now if I do the allocation before, it also throws the bad_alloc but before the exception is created.

我的例外概念的例外是:

谁在乎呢?如果我在我的程序bad_alloc,因为memory_leak或别的什么的(我说的是在个人电脑上没有微控制器的程序)我有其他问题。也许我可以找出一个bad_alloc发生,但在哪里?在一个函数(可能的1000一个)在我的alloc或的std ::字符串(当然我知道这是字符串,但......不可能操纵的记忆串......或者其消退)。

Who cares? If I have a bad_alloc in my program, because of a memory_leak or something else (I'm talking about programs on PCs not microcontrollers) I have other problems. Maybe I can figure out that a bad_alloc happened, but where? On my alloc during a function (one of maybe 1000) or in the std::string (well I know it's the string but ... no possibility to manipulate the memory of the string... or its to dissipated).

try {
  // where is the error???
  int *x = new int[100];  // here?
  ....
  int *y = new int[100];  // or here?
  ....
  int *z = new int[100];
  ....
  int *w = new int[100];
  ....
  int *t = new int[100];
  ....
  int *f = new int[100];

  ....

  std::string str("asdfasdfasdfasdfasdfasdfasdf"); // maybe here
}
catch (the error) {
  ....
}

然后呢?我会揣摩它是怎么回事?因此,我会使用的valgrind 不例外。

void foo() {
  int *i = new int[1];
  foo();
}

try {
  foo();
}
chatch( bad_boy ) {
  go_exception_handler_go(parameters); // oh, shit happens: also an stack_overflow may happend, cause stack is also full
}

还是我操作的ErrorMessage和日志吧,有什么明确将引发下一个bad_alloc。

Or shall i manipulate the errormessage and log it, what definitively would throw the next bad_alloc.

请不要误解我。因为我已经看到了boost ::例外,我已经重写我的异常类(直到等待一个答案),但我也觉得是不是真的有必要每一粒沙子回暖。

Please don't misunderstand me. Since I've seen the boost::exception I've rewritten my exception class (till waiting on an answer) but I also think it is not really necessary to pick up every grain of sand.

推荐答案

的意见基本上是告诉你不要使用可能会在异常抛出异常的结构。这是因为,如果你得到一个异常,而试图抛出一个异常,C ++运行时只会立即拨打终止()并杀死你的程序。

The advice is basically telling you "Don't use any construct that might throw an exception in an exception". That's because if you get an exception while trying to throw an exception, the C++ runtime will just immediately call terminate() and kill your program.

现在,如果(或)参与只是调用异常终止()反正(因为是未捕获的异常默认值),那么你并不真正需要的为此担心。例如,如果您的应用程序无法处理 bad_alloc (无法恢复从外存储器),那么你并不需要担心的拷贝构造函数(如为的std ::字符串)可能抛出它。

Now if (either) of the exceptions involved would just call terminate() anyways (as is the default for an uncaught exception), then you don't really need to worry about it. For example, if your application can't handle bad_alloc (can't recover from out-of-memory), then you don't need to worry about copy constructors (such as std::string) that might throw it.

但是,如果你希望能够赶上并从 bad_alloc恢复,你需要确保你没有任何异常拷贝构造函数可引起之一。如果你正在编写其他应用程序将使用一个库,你不应该假定应用程序不希望处理 bad_alloc

But if you want to be able to catch and recover from a bad_alloc, you need to ensure that none of your exception copy constructors can cause one. If you're writing a library that other applications will use, you should not assume that the application does not want to handle bad_alloc.

C ++ 11化妆这很容易通过移动构造函数(而不是拷贝构造函数)在可能的情况。自从搬到构造的std ::字符串不会抛出,你可以放心地使用性病例外:字符串在你的异常类型只要你正确地实现移动构造函数,并确保它们被使用。注意对象的初步建设,在罚球前pression抛出不是异常抛出过程的一部分,使构造函数可以抛出一个异常,而不会造成双重异常(并终止())。所以,如果您有:

C++11 make this much easier by using move constructors (instead of copy constructors) where possible. Since the move constructor for std::string never throws exceptions you can safely use a std:string in your exception type as long as you properly implement move constructors, and ensure that they are used. Note that the initial construction of the object to be thrown in a throw expression is NOT part of the exception throwing process, so that constructor can throw an exception without causing a double exception (and terminate()). So if you have:

throw some_function();

some_function 可能会抛出异常(如 bad_alloc ),而无需返回抛出的对象,这很好。如果没有抛出异常(并返回一个有效的对象),为异常型移动构造函数将被使用(如果可用)的异常抛出过程,这一举动构造函数不能抛出异常。

some_function might throw an exception (such as bad_alloc) without returning an object to be thrown and that's fine. If it doesn't throw an exception (and returns a valid object), the move constructor for the exception type will be used (if available) for the exception throwing process, and that move constructor must not throw an exception.

完全独立于上述情况,只要您拨打您需要确保只有一个点会叫删除在每一个可能的情况下,否则你会泄漏内存(或崩溃从双删除)。这将成为棘手你调用一个函数的任何时间,然后做别的东西,可能会引发异常(如呼叫再次)。如果这种情况发生在构造函数中,对象析构函数将不会被调用(尽管基类和析构函数领域会),所以你不能做清理工作在析构函数当你试图用你的例子做。

Completely independent of the above, whenever you call new you need to ensure that exactly one spot will call delete in every possible case, or you'll leak memory (or crash from a double delete). This becomes tricky any time you have a function that calls new and then does something else that might throw an exception (such as call new again). If this happens in a constructor, the destructor for the object will not be called (though destructors for base classes and fields will be), so you can't do the cleanup in the destructor as you are trying to do with your example.

幸运的是的std ::的unique_ptr 的存在使这要容易得多。如果你写你的异常类:

Fortunately std::unique_ptr exists to make this much easier. If you write your exception class as:

struct xexception {
  std::unique_ptr<int[]> ttt[10];
  xexception() {
    ttt[0].reset(new int[0xfffffffL]);
    ttt[1].reset(new int[0xfffffffL]);
    ttt[2].reset(new int[0xfffffffL]);
    ttt[3].reset(new int[0xfffffffL]);
    ttt[4].reset(new int[0xfffffffL]);
    ttt[5].reset(new int[0xfffffffL]);
    ttt[6].reset(new int[0xfffffffL]);
    ttt[7].reset(new int[0xfffffffL]);
    ttt[8].reset(new int[0xfffffffL]);
    ttt[9].reset(new int[0xfffffffL]);
  }
};

它应该工作,而不是造成内存泄漏。

it should work and not leak memory.

这篇关于(不)使用的std ::在异常字符串的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆