我正在设计一个类似std::vector
的课程用于自学教学目的,但我在构造函数内部遇到了一个难以解决的内存分配问题。
std::vector
的容量构造函数非常方便,但是有可能抛出std::bad_alloc
异常,并用它来删除整个程序。
我正在努力决定处理容量构造函数失败的不太可能的情况的最优雅方法,或者最好通知用户通过使用构造函数,他们同意数据结构能够取消整个计划都是例外。
我的第一个想法是每当调用构造函数时添加一个编译时警告,提醒构造函数可能失败,并且他们应该确保要么处理它,要么意识到使用构造函数所涉及的风险。
这个解决方案似乎很糟糕,因为如果在全球范围内应用它会引起太多警告,并给人留下不好的印象。
我的第二个想法是使构造函数变为私有,并且需要通过类似于Singleton模式的静态“requestConstruct”方法来访问构造函数。
此解决方案使界面看起来很奇怪。
我还有一些想法,但所有这些想法似乎都会破坏界面的“感觉”。这些想法是:
boost::optional
还有一个很有可能没有足够的内存来实际捕获异常。这个程序似乎没有抓住它(这可能与有足够的内存有关,也可能没有。)
#include <stdint.h>
#include <exception>
#include <iostream>
#include <stdlib.h>
int main(int argc, char **argv)
{
uint_fast32_t leaked_bytes = 0;
while (1) {
try {
new uint8_t;
} catch (const std::bad_alloc& e) {
std::cout << "successfully leaked" << leaked_bytes << " bytes." << '\n';
exit(0);
} catch (const std::exception& e) {
std::cout << "I caught an exception, but not sure what it was...\n";
exit(0);
}
++leaked_bytes;
}
}
这个程序确实允许我在程序终止之前处理失败:
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
int main(int argc, char **argv)
{
uint_fast32_t bytes_leaked = 0;
while (1) {
if (malloc(1) == 0) {
printf("leaked %u bytes.\n", bytes_leaked);
exit(0);
}
++bytes_leaked;
}
return 0;
}
nothrow也能正常工作:
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <new>
int main(int argc, char **argv)
{
uint64_t leaked_bytes = 0;
while (1) {
uint8_t *byte = new (std::nothrow) uint8_t;
if (byte == nullptr) {
printf("leaked %llu bytes.\n", leaked_bytes);
exit(0);
}
++leaked_bytes;
}
return 0;
}
编辑:
我想我找到了解决这个问题的方法。在主流程上存储动态数据结构存在固有的天真。也就是说,这些结构不能保证成功,并且可能在任何时候都会破裂。
最好在另一个进程中创建所有动态结构,并在出现意外问题时使用某种重启策略。
这确实会增加与数据结构进行通信的开销,但它会阻止主程序管理数据结构可能出错的所有内容,这可以快速占用main中的大部分代码。
结束编辑。
是否有正确的方法来处理此类动态类中的分配问题,提高用户对这些风险的认识?
答案 0 :(得分:0)
根据提供的反馈,我确信没有优雅的方法来阻止管理动态内存的类在不引入复杂性的情况下删除程序。
抛出异常是有问题的,因为一旦你的类没有可以分配的内存,它就可能无法处理用户可以捕获的std :: bad_alloc。
从更多的思考,我开始意识到防止程序从它使用的模块崩溃的一种方法是将使用这些模块的程序部分移动到另一个进程,并让该进程与主进程,如果其他进程以某种方式发生故障,您可以重新启动该进程并发出另一个请求。
只要您使用任何能够在极端情况下失败的类,除非您能够控制其极端情况,否则无法阻止它们失败。
对于动态内存,很难准确控制进程可以访问的动态内存,它具有的分配策略以及应用程序使用的总内存量。因此,如果不严格控制您使用的连续内存池,或者将数据结构移动到另一个可以在失败时重新启动的进程进行管理,则很难对其进行控制。
答案:提供管理动态内存的类的简单接口/实现对本身是不可能的。你要么有一个简单的接口/实现,它将取消你的程序,如std :: vector;或复杂的接口/实现,需要以下之一或更聪明的东西: