【问题标题】:Is there a way to prevent Memory Leaks in a Derived Class without modifying the Base Class?有没有办法在不修改基类的情况下防止派生类中的内存泄漏?
【发布时间】:2016-05-03 16:05:33
【问题描述】:

所以我有一个看起来像这样的基类:

class base {
public:
    base() {
        std::cout << "We created a base!" << std::endl;
    }

    ~base() {
        std::cout << "We destroyed a base!" << std::endl;
    }
};

我有一个如下所示的派生类:

class leaks_memory : public base {
public:
    leaks_memory(size_t count) :
    memory(new int[count]), size_of_memory(count)
    {
        announce();
    }

    leaks_memory(const leaks_memory & lm) :
    leaks_memory(lm.size_of_memory)
    {
        std::copy(lm.memory, lm.memory + size_of_memory, memory);
    }

    void swap(leaks_memory & lm) noexcept {
        std::swap(lm.memory, memory);
        std::swap(lm.size_of_memory, size_of_memory);
    }

    leaks_memory(leaks_memory && lm) {
        swap(lm);
    }

    leaks_memory & operator=(leaks_memory lm) {
        swap(lm);
        return *this;
    }

    ~leaks_memory() {
        delete[] memory;
        dennounce();
    }

    int & operator[](size_t index) {
        return memory[index];
    }

    const int & operator[](size_t index) const {
        return memory[index];
    }
private:
    int * memory;
    size_t size_of_memory;

    void announce() const noexcept {
        std::cout << "We created a Leaks Memory!" << std::endl;
    }

    void dennounce() const noexcept {
        std::cout << "We destroyed a Leaks Memory!" << std::endl;
    }
};

现在,这些都不是问题,直到我编写如下所示的代码:

int main() {
    std::unique_ptr<base> base_ptr;
    std::atomic_bool done = false;
    std::thread input_thread{ [&done] {
        std::getline(std::cin, std::string());
        done = true;
    } };
    while (!done) {
        base_ptr = std::make_unique<leaks_memory>(20'000);
    }
    input_thread.join();
    return 0;
}

这段代码每次循环迭代都会泄漏 20kb,因为 leaks_memory 析构函数永远不会被调用!

现在,很明显,我可以通过编辑 base 来解决这个问题:

    virtual ~base() {
        std::cout << "We destroyed a base!" << std::endl;
    }

确实,如果我在进行此更改后运行相同的代码,我将不再有这种内存泄漏。

但是,如果我无法编辑 base 类怎么办?有没有办法在不完全改变执行代码的设计的情况下防止内存泄漏?

【问题讨论】:

  • 在基类中有一个 virtual 析构函数。
  • @MichaelWalz:不一定,它确实会增加性能损失。任何可能被继承的类都应该是虚拟的。
  • @MichaelWalz 不一定,很多情况下我不需要虚拟析构函数,想避免生成vtable。
  • @Xirema 好吧,那么答案是:不,不改变基类是不可能的。
  • 您的程序有未定义的行为,而不是内存泄漏。如果您使用带有智能指针的自定义删除器(可能必须使用 shared_ptr 或您自己品牌的 unique_ptr),则可以摆脱它。

标签: c++ inheritance memory-management memory-leaks virtual-functions


【解决方案1】:

C++11 智能指针具有自定义删除器,这在某种程度上放宽了在每个层次结构的根处具有虚拟析构函数的要求。

可惜这个特性不好用,因为删除器的类型是shared_ptr和unique_ptr的模板参数。所以为了隐藏最终对象的类型,需要一点类型擦除:

void derived_deleter(base* p) { delete static_cast<derived*>(p); }

std::unique_ptr<base, decltype(&derived_deleter)> base_ptr (new derived, derived_deleter);

似乎没有办法使用std::make_unique。

【讨论】:

  • 这适用于 base_ptr(nullptr,nullptr) 然后 base_ptr.swap(temp_base_ptr) 在循环中。
【解决方案2】:

如果您可以使用const 引用,另一种选择可能是使用"most important const"

以下将创建对基的引用,但会调用派生的析构函数。

leaks_memory factory()
{
    return leaks_memory(20'000);
}

int main()
{
    //std::unique_ptr<base> base_ptr;
    std::atomic_bool done = false;
    std::thread input_thread{[&done] {
        std::getline(std::cin, std::string());
        done = true;
    }};
    while (!done) {
        //base_ptr = std::make_unique<leaks_memory>(20'000);
        const base& base_const_ref = factory();
    }
    input_thread.join();
    return 0;
}

【讨论】:

    猜你喜欢
    • 2014-03-22
    • 1970-01-01
    • 1970-01-01
    • 2021-09-08
    • 2021-12-19
    • 1970-01-01
    • 1970-01-01
    • 2013-03-26
    • 1970-01-01
    相关资源
    最近更新 更多