Как предотвратить утечки памяти в коде, который использует std :: shared_ptr - PullRequest
0 голосов
/ 18 сентября 2018

Недавно я работал над интерпретатором схемы в c ++.Для версии 2 я переписал ее, чтобы она использовала связанный список внутренних клеток.Все идет нормально.Сейчас я реализую приложение, которое должно взять два списка и объединить их в два списка.Код, показанный ниже, работает.Он выводит:

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

как следует.Однако Вальгринд показывает, что это утечка памяти.(определенно потеряно: 80 байтов в 2 блоках косвенно потеряно: 240 байтов в 8 блоках) Я думаю, это из-за всех объектов, которые я создаю в 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 голосов
/ 18 сентября 2018

Как предотвратить утечки памяти в коде, который использует std :: shared_ptr

Избегать необработанных new и delete.Всегда используйте std::unique_ptr или std::shared_ptr, когда вам нужен указатель-владелец.Используйте сырые указатели только как не владеющие дескрипторами.Используйте std::make_shared и std::make_unique для создания объектов, которые принадлежат интеллектуальным указателям.Не звоните .release() на unique_ptr с.Вот и все.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...