如何防止使用std :: shared_ptr

时间:2018-09-18 16:20:50

标签: c++ c++17

最近,我一直在使用c ++进行方案解释器。对于版本2,我已对其进行了重写,因此它在内部使用了con单元格的链接列表。到目前为止,一切都很好。现在,我正在实现append,它应该包含两个列表并将它们连接成两个一个列表。下面显示的代码有效。它输出:

(1,2)
(3,4)
(1,2,3,4)

应当如此。但是valgrind揭示它正在泄漏内存。 (绝对丢失:2个字节中的80个字节,间接丢失:8个块中的240个字节)我认为这是因为我在main()中创建的所有对象的指针都被复制到Cons的构造函数中,并且该副本永远不会被删除。这是正确的吗?

所以我应该以引用的方式传递它们,或者以某种方式正确移动它们?不幸的是,我这样做的尝试导致了更多错误。谁能告诉我如何使此代码无泄漏?

typedef enum { CONS = 0, ATOM = 1, FUNCTION = 2, LAMBDA = 3 } Type;

class Expression {
public:
    explicit Expression(Type type) : type_{type} {
    }

    virtual ~Expression() {}

    Type type_;
};

class Cons : public Expression {
public:
    Cons(Expression* car = nullptr, Expression* cdr = nullptr) :
    Expression(Type::CONS), car_{car}, cdr_{cdr} {
    }

    ~Cons() {
        if (car_) {
            delete car_;
        }
    }

    Expression* car_;
    std::shared_ptr<Expression> cdr_;
};

class Atom : public Expression {
public:
    Atom(const char* value) : Expression(Type::ATOM), value_{value} {
    }

    Atom(std::string value) : Atom(value.c_str()) {
    }

    std::string value_;
};

std::ostream& operator<<(std::ostream& out, Expression* exp) {
    switch(exp->type_) {
        case Type::ATOM:
            out << dynamic_cast<Atom*>(exp)->value_;
            break;
        case Type::CONS: {
            out << "(";
            auto current = dynamic_cast<Cons*>(exp);
            while (current) {
                out << current->car_;
                if (current->cdr_) {
                    out << ' ';
                }
                current = dynamic_cast<Cons*>(current->cdr_.get());
            }
            out << ")";
            break;
        }
        case Type::FUNCTION:
        case Type::LAMBDA:
            break;
    }

    return out;
}

void traverse(Expression* exp, std::function<void(Expression*)> process) {
    if (exp) {
        if (exp->type_ == Type::CONS) {
            auto cell = dynamic_cast<Cons*>(exp);
            traverse(cell->car_, process);
            traverse(cell->cdr_.get(), process);
        } else {
            process(exp);
        }
    }
}

Expression* append(Expression* first, Expression* second) {
    Cons* ret = nullptr;
    auto add_to_ret = [&ret](Expression* cell) -> void {
        if (ret == nullptr) {
            ret = dynamic_cast<Cons*>(new Cons(cell));
        } else {
            auto temp = ret;
            while(temp->cdr_) {
                temp = dynamic_cast<Cons*>(temp->cdr_.get());
            }
            temp->cdr_.reset(new Cons(cell));
        }
    };

    traverse(first, add_to_ret);
    traverse(second, add_to_ret);

    return ret;
}

int main() {
    Expression* list1 = new Cons(
            new Atom("1"),
            new Cons(
                new Atom("2"),
                nullptr
            )
        );
    std::cerr << list1 << '\n';

    Expression* list2 = new Cons(
            new Atom("3"),
            new Cons(
                new Atom("4"),
                nullptr
            )
        );
    std::cerr << list2 << '\n';

    Expression* joined = new Cons(
        list1,
        nullptr
    );

    joined = append(joined, list2);

    std::cout << joined << '\n';

    if (joined) {
        delete joined;
    }

    return EXIT_SUCCESS;
}

1 个答案:

答案 0 :(得分:2)

  

如何防止使用std :: shared_ptr的代码中的内存泄漏

避免使用原始newdelete。需要拥有指针时,请始终使用std::unique_ptrstd::shared_ptr。仅将原始指针用作非所有权句柄。使用std::make_sharedstd::make_unique创建智能指针拥有的对象。不要在.release()上致电unique_ptr。就是这样。