(不)使用std ::在异常的字符串((not) using std::string in exce

2019-08-19 18:35发布

我一直在读书,我不应该扔std::string或一些其他类分配内存。 像这里或更重要的是这里的第3点- 不要嵌入std::string对象

所以现在我想插入的boost ::例外,我的项目,我看到了什么: 很多字符串 。

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

如果我有不能被硬编码,像萨法德在配置文件中的参数,我怎样才能把它们变成一个例外,在不使用std::string

或者是指引不使用std::string只有确实使用std::string尽可能少准则? 我有点糊涂了...

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


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

我尝试这样做:

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的,但在创建异常之前。


我对例外的概念例外是:

谁在乎? 如果我在我的节目一个bad_alloc的,因为memory_leak或其他东西的(我说的是在个人电脑上没有微控制器的程序)我有其他问题。 也许我可以找出一个bad_alloc的发生,但在哪里? 上的功能在我页头(可能的1000一个),或在std::string (好,我知道它的绳子,但......没有可能操作字符串的记忆......或者其消退)。

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的。

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

Answer 1:

建议基本上是告诉你“不要使用可能会在异常抛出异常的任何构造”。 这是因为,如果你得到一个异常,而试图抛出一个异常,C ++运行时只会立即调用terminate()和杀死你的计划。

现在,如果(或)有关的例外的只是调用terminate()反正(如对于未捕获的异常默认值),那么你并不真正需要担心。 例如,如果你的应用程序无法处理bad_alloc (不能从外的内存中恢复),那么你并不需要担心的拷贝构造函数(如std::string )可能抛出它。

但是,如果你希望能够赶上并从恢复bad_alloc ,你需要确保你没有例外拷贝构造函数会导致一个。 如果你正在编写其他应用程序将使用一个库,你不应该假定应用程序不希望处理bad_alloc

C ++ 11化妆这么多的使用移动构造函数(而不是拷贝构造函数)在可能的情况更容易。 由于对移动构造函数std::string不会抛出异常,你可以放心地使用std:string只要你正确地实现移动构造函数在你的异常类型,并确保它们被使用。 请注意,对象的初始建设中投掷表达被抛出不是异常抛出过程的一部分,从而使构造函数可以抛出异常,而不会造成双异常(和终止())。 所以,如果你有:

throw some_function();

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


完全独立于上述情况,只要您拨打new ,你需要确保只有一个点会打电话给delete在每一个可能的情况下,否则你会(从双删除或崩溃)泄漏内存。 这将成为棘手你调用一个函数任何时候new ,然后(如呼叫做别的,可能会引发异常new一次)。 如果这发生在一个构造函数,该对象的析构函数将不会被调用(尽管基类和字段析构函数会),所以,当你想用你的榜样,做你无法做清理工作在析构函数。

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

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]);
  }
};

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



Answer 2:

虽然我认为没有使用std::string为核心,基本异常可能是一个很好的指引,我不认为面向用户的库/应用程序应遵循一定这个。

可能还有其他原因,但你打主之一:你想向用户指示(或开发商)上下文有意义的信息,你往往不能用仅有的文本字符串做。 动态分配必须发生在为了做到这一点。 如果由于某种原因,你有一个bad_alloc ,你很可能已经在该区开始用,所以也没有买/失去你任何东西。

编辑:

顺便说一句:的析构函数std::exception被标记为虚拟的一个原因!



文章来源: (not) using std::string in exceptions