c++ 如果第二个malloc在同一个构造函数中失败,我应该怎么办

oprakyz7  于 2023-01-15  发布在  其他
关注(0)|答案(4)|浏览(172)

注意这个问题的基础是使用两个malloc() ......虽然建议完全不使用malloc()是完全有效的,并且会带来更好的设计,但这不是问题的重点。也许你可以认为我的客户是一个精神病患者,而我得到报酬是为了使用两个malloc()

=====问题本身来了=====
假设我被下面的类卡住了,无法切换到vector<int>unique_ptr<int>等特性,虽然不是“现代的”,但无论malloc()是否成功,它都应该仍然工作而不会泄漏任何内存:

class Foo {
    private:
        int *ptr;
    public:
        Foo () {
            this->ptr = (int*)malloc(sizeof(int) * ARR_SIZE);
            if (this->ptr == NULL) {
                std::bad_alloc exception;
                throw exception;
            }
        }
        ~Foo () {
            free(this->ptr);
        }

};

如果我需要在同一个构造函数中两次malloc(),就会出现这个问题:

class Foo {
    private:
        int *ptr0;
        int *ptr1;
    public:
        Foo () {
            this->ptr0 = (int*)malloc(sizeof(int) * ARR_SIZE_0);
            if (this->ptr0 == NULL) {
                std::bad_alloc exception;
                throw exception;
            }
            this->ptr1= (int*)malloc(sizeof(int) * ARR_SIZE_1);
            if (this->ptr1== NULL) {
                free(this->ptr0); // QUESTION: Is this needed and does it follow the best practice?
                std::bad_alloc exception;
                throw exception;
            }
        }
        ~Foo () {
            free(this->ptr0);
            free(this->ptr1);
        }

};

我知道在第二种情况下,我们创建两个类,每个类 Package 一个指针,这样可以完全遵循RAII的原则,并且不需要构造函数中的上述“C风格”free()
问题是,不管出于什么原因,我必须在构造函数中有两个malloc(),我的设计是否足够好(即,没有泄漏内存,也不太冗长)?

rn0zuynd

rn0zuynd1#

这是一个启用了RAII的版本。

struct Free
{
   void operator()(void* p) const { std::free(p); }
};

template <typename T, typename Deleter = Free>
std::unique_ptr<T, Deleter> raii_malloc()
{
   T* obj = static_cast<T*>(std::malloc(sizeof(T)));
   if (obj == nullptr) throw std::bad_alloc();
   return std::unique_ptr<T, Deleter>(obj);
}

template <typename T, typename Deleter = Free>
std::unique_ptr<T[], Deleter> raii_malloc_array(size_t size)
{
   T* obj = static_cast<T*>(std::malloc(sizeof(T) * size));
   if (obj == nullptr) throw std::bad_alloc();
   return std::unique_ptr<T[], Deleter>(obj);
}

template <typename T>
using fptr = std::unique_ptr<T, Free>;

现在,您的类看起来如下所示:

class Foo
{
      fptr<int[]> ptr0;
      fptr<int[]> ptr1;
   public:
      Foo() : ptr0{raii_malloc_array<int>(ARR_SIZE_0)},
              ptr1{raii_malloc_array<int>(ARR_SIZE_1)}
      {}
      // no destructor needed
};

注意这个版本是不可复制的。如果你需要复制,添加一个自定义的复制构造函数和/或赋值操作符(仍然不需要析构函数)。
您可以将其用于包含内部指针或其他需要释放的资源的C对象,您只需要提供一个不同的删除器,而不是Free
如果unique_ptr不可用,滚动自己的简化版本是极其容易的。

ttvkxqim

ttvkxqim2#

1.使用std::vectorstd::unique_ptr帮助管理内存。
1.除非万不得已,否则不要使用malloc
1.确保你的类可以安全地复制/移动,或者不可复制/移动(例如,删除构造函数operator=)。
1.考虑一下您处理内存不足情况的可能性有多大(当系统内存不足时,您是否要打开一个文件并记录它?),如果分配失败,可能会终止。

kxkpmulp

kxkpmulp3#

避免这个问题的一个相当简单的方法是一次性为成员变量分配必要的内存。

class Foo
{
    public:
        Foo()
        {
            ptr0 = new int[ARR_SIZE0 + ARR_SIZE1];
            ptr1 = ptr0 + ARR_SIZE0;
        }

        ~Foo()
        {
            delete ptr0[];
        }

        // PLEASE insert the rest of the
        // necessary constructors
        // and assignment operators...

    private:
        int * ptr0;
        int * ptr1;
};

这里使用new而不是malloc,因为如果您必须进行手动分配,至少使用C++构造进行分配。

vwkv1x7d

vwkv1x7d4#

你更新后的问题使问题的内容更加清晰。我觉得我的第一个答案仍然有价值,但这个答案完全是另一个方向,这就是为什么我把它作为一个单独的答案写下来。
它可以归结为:
如果我要从构造函数中抛出异常,我是否必须主动释放先前在该构造函数中手动分配的资源?

是的,您需要,对于任何资源,您同样需要在析构函数中使用free/close/disconnect/...。

如果一个构造函数失败(比如抛出),对象就没有被构造。因此,它也没有被析构--它的析构函数永远不会被调用。你将失去对ptr0ptr1的访问。你的异常可能被捕获,程序可能继续--但是你会泄漏内存。
所以,同样,是的,请**在抛出异常之前,**释放任何你已经在构造函数中分配的,要在析构函数中释放的资源。
不过,所有其他的评论和意见也都是正确的。你应该努力不要混合C结构和C结构。编写习惯的C,而不是被轻蔑地称为“带类的C”,将导致更好的可读性,更稳定,可能更有效的代码。

相关问题