我正在为自学目的设计类似std::vector
的类,但是遇到了构造函数内部的内存分配难题。std::vector
的容量构造函数非常方便,但是会引发std::bad_alloc
异常并用它删除整个程序的代价。
我正在努力决定用什么最优雅的方式来处理不太可能出现的容量构造函数失败的情况,或者最好是通知用户,通过使用构造函数,他们同意数据结构能够删除整个程序通过一个异常(exception)。
我的第一个想法是在每次调用构造函数时都添加一个编译时警告,提醒构造函数可能会失败,并且他们应该确保对其进行处理,或者意识到使用该构造函数会带来的风险。
该解决方案似乎很糟糕,因为如果将其应用到全局范围内,将会引起过多的警告并给人留下不良印象。
我的第二个想法是使构造函数私有(private),并要求通过类似于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中的大部分代码。
结束编辑。
是否有适当的方法来处理此类动态类中的分配问题,提高我的用户对这些风险的认识?
最佳答案
根据所提供的反馈,我相信没有一种优雅的方法可以防止管理动态内存的类在不引入复杂性的情况下占用您的程序。
引发异常是有问题的,因为一旦您的类没有内存就可以分配,它可能就无法处理用户可以捕获的std::bad_alloc。
经过更多的思考,我意识到一种防止程序从其使用的模块中崩溃的方法是将使用这些模块的程序部分移至另一个进程,并使该进程与主进程共享内存,这样,如果其他进程失败了,您可以重新启动该进程并发出另一个请求。
只要您使用能够在极端情况下失败的任何类,就无法防止它们失败,除非您能够控制它们的极端情况。
对于动态内存,很难精确地控制您的进程可以访问的动态内存的数量,其拥有的分配策略以及应用程序已使用的内存总量。因此,如果不更严格地使用您所使用的连续内存池,或者很难将数据结构移到要由其他进程管理的数据中,该进程可以在发生故障时重新启动,那么很难控制它。
回答:本质上不可能同时提供管理动态内存的类的简单接口(interface)/实现对。您或者有一个简单的接口(interface)/实现,它将关闭您的程序,例如std::vector;。或复杂的界面/实现需要以下一项或更巧妙的选择:
关于c++ - 构造函数内部的内存分配?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/36492854/