Этот рекурсивный класс нуждается в собственном деструкторе? - PullRequest
0 голосов
/ 17 апреля 2019

Я получил утечку памяти, которую отследил до этого класса JSON.

Я уже забочусь об удалении каждого возможного объекта после использования, но я думаю, что оператор delete не мог удалить подструктуру 'object_val' и 'array_val'.

Это имеет какой-то смысл? Как я могу "безоговорочно" удалить структуру?

//---------------------------------------------------------------------------
#ifndef JSONH
#define JSONH

#include <System.Classes.hpp>
#include <System.StrUtils.hpp>

#include <System.JSON.Readers.hpp>
#include <System.JSON.Types.hpp>
#include <System.JSON.Utils.hpp>
#include <System.JSON.Writers.hpp>
#include <System.JSON.Builders.hpp>

#include <stack>
#include <fstream.h>
#include <utility>
#include <iostream>

class JSON;
class JSON {
    public: 

        // JSON types
        enum Type {
            __INT,
            __BOOLEAN,
            __FLOAT,
            __STRING,
            __OBJECT,
            __ARRAY,
            __NULL
        };

        // Static functions
        static JSON * JSON::parse(UnicodeString str);
        static JSON & JSON::parser(TJsonTextReader& json_reader);
        static bool JSON::isNumber(const std::string& s);


        JSON(){}
        ~JSON(){}

        // Member attributtes
        Type type;
        int                                    int_val;
        bool                                  bool_val;
        float                                float_val;
        UnicodeString                       string_val;
        stack<pair<UnicodeString, JSON*> > *object_val;
        stack<JSON*>                        *array_val;

        // Member functions
        JSON * copy();
        JSON * find(UnicodeString path);
        JSON * map(UnicodeString key);
        JSON * set(UnicodeString prop, JSON *value);
        JSON * push(JSON *value);
        JSON * filter(JSON *params);
        JSON * find_by(JSON *params);

        UnicodeString dump();
        UnicodeString stringify();
        int size();


};

//------------------------------------------------------
#endif


#pragma hdrstop

#include "JSON.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)

using namespace std;

JSON * JSON::parse(UnicodeString str){
    if (str == "") {
        throw "invalid JSON: " + str;
    }
    TStringReader *string_reader = new TStringReader(str);
    TJsonTextReader *json_reader = new TJsonTextReader(string_reader);
    JSON *result = new JSON;
    *result = JSON::parser(*json_reader);
    delete string_reader;
    delete json_reader;
    return result;
}

JSON & JSON::parser(TJsonTextReader &json_reader){

    if(json_reader.TokenType == TJsonToken::None){
        json_reader.Read();
        return JSON::parser(json_reader);
    }

    JSON *json = new JSON;

    //INTEGER
    if(json_reader.TokenType == TJsonToken::Integer){
        json->type = JSON::__INT;
        json->int_val = json_reader.Value.AsInteger();
        return *json;
    }

    //FLOAT
    else if(json_reader.TokenType == TJsonToken::Float){
        json->type = JSON::__FLOAT;
        json->float_val = json_reader.Value.AsExtended();
        return *json;
    }

    //STRING
    else if(json_reader.TokenType == TJsonToken::String){
        json->type = JSON::__STRING;
        json->string_val = json_reader.Value.AsString();
        return *json;
    }

    //BOOLEAN
    else if(json_reader.TokenType == TJsonToken::Boolean){
        json->type = JSON::__BOOLEAN;
        json->bool_val = json_reader.Value.AsBoolean();
        return *json;
    }

    // OBJECT
    else if(json_reader.TokenType == TJsonToken::StartObject){
        json->type = JSON::__OBJECT;
        json->object_val = new stack<pair<UnicodeString, JSON*> >;
        while(json_reader.Read() && json_reader.TokenType != TJsonToken::EndObject){
            UnicodeString key = json_reader.Value.AsString();
            json_reader.Read();
            JSON *val = new JSON;
            *val = JSON::parser(json_reader);
            json->object_val->push(make_pair(key, val));
        }
        return *json;
    }

    // ARRAY
    else if(json_reader.TokenType == TJsonToken::StartArray){
        json->type = JSON::__ARRAY;
        json->array_val = new stack<JSON*>;
        while(json_reader.Read() && json_reader.TokenType != TJsonToken::EndArray){
            JSON *val = new JSON;
            *val = JSON::parser(json_reader);
            json->array_val->push(val);
        }
        return *json;
    }

    //NULL
    else if(
            json_reader.TokenType == TJsonToken::Null
        ||  json_reader.TokenType == TJsonToken::Undefined
    ){
        json->type = JSON::__NULL;
        return *json;
    }
}

bool JSON::isNumber(const std::string& s) {
    std::string::const_iterator it = s.begin();
    while (it != s.end() && std::isdigit(*it)) ++it;
    return !s.empty() && it == s.end();
}

JSON * JSON::find(UnicodeString path){
    TStringDynArray slice = SplitString(path, ".");

    UnicodeString next = "";

    if(slice.Length > 1){
        for (int i = 1; i < slice.Length; ++i) {
            next += slice[i];
            if (i != slice.Length-1) {
                next += ".";
            }
        }
    } 

    if (type == __OBJECT){
        for (int i = 0; i < object_val->size(); i++) {
            if (object_val->c[i].first == slice[0]){
                if (slice.Length > 1) {
                    return object_val->c[i].second->find(next);
                }
                else {
                    return object_val->c[i].second;
                }
            }
        }
    }
    else if(type == __ARRAY){
        wstring ws(slice[0].c_str());
        string str(ws.begin(), ws.end());

        if (JSON::isNumber(str)){
            if (slice.Length > 1) {
                return array_val->c[slice[0].ToInt()]->find(next);
            }
            else {
                return array_val->c[slice[0].ToInt()];
            }
        }
    }
    return NULL;
}

UnicodeString JSON::stringify(){
    //INTEGER
    if(type == JSON::__INT){
        return (UnicodeString) int_val;
    }

    //FLOAT
    else if(type == JSON::__FLOAT){
        return (UnicodeString) float_val;
    }

    //STRING
    else if(type == JSON::__STRING){
        return (UnicodeString) "\""+ string_val + "\"";
    }

    //BOOLEAN
    else if(type == JSON::__BOOLEAN){
        if(bool_val){
            return (UnicodeString) "true";
        }
        else {
            return (UnicodeString) "false";
        }
    }

    // OBJECT
    else if(type == JSON::__OBJECT){
        if (object_val->size()){

            UnicodeString str = "{";
            for (int i = 0; i < object_val->size(); ++i){
                str += "\"" + object_val->c[i].first + "\":" + object_val->c[i].second->stringify();
                if (object_val->size()-1 != i){
                    str += ", ";
                }
            }
            str += "}";
            return str;
        }
        else {
            return (UnicodeString) "{}";
        }
    }

    // ARRAY
    else if(type == JSON::__ARRAY){
        if (array_val->size()){

            UnicodeString str = "[";
            for (int i = 0; i < array_val->size(); ++i){
                str += array_val->c[i]->stringify();
                if (array_val->size()-1 != i){
                    str += ", ";
                }
            }
            str += "]";
            return str;
        }
        else {
            return (UnicodeString) "[]";
        }
    }

    //NULL
    else if(type == JSON::__NULL){
        return (UnicodeString) "null";
    }
}

UnicodeString JSON::dump(){
    UnicodeString d = stringify();
    return StringReplace(d, "\"", "", TReplaceFlags() << rfReplaceAll);
}

JSON * JSON::map(UnicodeString key){
    if (type != JSON::__ARRAY){
        throw "Not a array";
    }

    UnicodeString str_result = "[";

    for (int i = 0; i < array_val->size(); ++i){
        JSON *val = array_val->c[i];
        if(val->type != JSON::__OBJECT){
            throw "Not a array of objects";
        }
        else {
            str_result += val->find(key)->stringify();
        }
        if(i != array_val->size()-1){
            str_result += ',';
        }
    }
    str_result += "]";
    return JSON::parse(str_result);
}

int JSON::size(){
    if(type == JSON::__OBJECT){
        return object_val->size();
    }
    else if (type == JSON::__ARRAY){
        return array_val->size();
    }
    else if (type == JSON::__STRING){
        return string_val.Length();
    }
    else {
        return 0;
    }
}

JSON * JSON::set(UnicodeString prop, JSON *value){
    if (this->type == JSON::__OBJECT) {
        this->object_val->push(make_pair(prop, value));
    }
    else {
        throw "This is not an object";
    }
    return this;
}

JSON * JSON::push(JSON *value){
    if (this->type == JSON::__ARRAY) {
        this->array_val->push(value);
    }
    else {
        throw "This is not an array";
    }
    return this;
}

JSON * JSON::copy(){
    JSON *copy;

    if(type == JSON::__ARRAY){
        copy = JSON::parse("[]");
        for (int i = 0; i < size(); ++i){
            copy->push(array_val->c[i]->copy());
        }
    }
    if(type == JSON::__OBJECT){
        copy = JSON::parse("{}");
        for (int i = 0; i < size(); ++i){
            UnicodeString key = this->object_val->c[i].first;
            JSON *val = this->object_val->c[i].second->copy();
            copy->set(key, val);
        }
    }
    else{
        copy = JSON::parse(this->stringify());
    }
    return copy;
}


JSON * JSON::filter(JSON *params){
    if(type != JSON::__ARRAY)
        throw "this is not an array";

    if (params->type != JSON::__OBJECT)
        throw "params is not an object";


    JSON *result = JSON::parse("[]");
    JSON *this_value;
    for (int i = 0; i < this->array_val->size(); ++i){
        for (int it = 0; it < params->size(); ++it){
            this_value = this->array_val->c[i]->find(params->object_val->c[it].first);
            UnicodeString str_params = params->stringify();
            UnicodeString str_this = this->stringify();
            UnicodeString key_test = params->object_val->c[it].first;
            UnicodeString this_test = this_value->stringify();
            UnicodeString params_test = params->object_val->c[it].second->stringify();
            if(this_value != NULL){
                if(this_value->stringify() == params->object_val->c[it].second->stringify()){
                    result->array_val->push(this->array_val->c[i]);
                }
            }
        }
    }
    return result;
}

JSON * JSON::find_by(JSON *params){
    JSON *filtered = filter(params);
    if(filtered->size()){
        return filtered->find("0");
    }
    return NULL;
}

Я могу гарантировать удаление каждого объекта JSON в приложении. Используется только дважды с JSON :: parse (UnicodeString (request)).

UPDATE: Решено с помощью следующего деструктора:

        ~JSON(){
            if (type == JSON::__OBJECT) {
                for (int i = 0; i < object_val->size(); ++i)
                    if(object_val->c[i].second)
                        delete object_val->c[i].second;

                delete object_val;
            }

            if(type == JSON::__ARRAY){
                for (int i = 0; i < array_val->size(); ++i)
                    if(array_val->c[i])
                        delete array_val->c[i];

                delete array_val;
            }
        }

Ответы [ 2 ]

0 голосов
/ 18 апреля 2019

В вашем коде МНОГИЕ утечки.

Ваш метод parser() возвращает ссылку JSON& вместо указателя JSON* (как это делает ваш метод parse()). Вы выделяете новый JSON объект через new, а затем возвращаете его вызывающей стороне путем разыменования указателя, а затем вызывающая сторона копирует этот объект в другой выделенный JSON объект (но у вас нет правильного оператора копирования!) и не delete исходный объект. Вы делаете эту ошибку много раз внутри parse() и parser().

У вас также происходит утечка памяти в вашем методе find_by(). Ваш метод filter() возвращает указатель JSON* на объект new 'ed JSON, но find_by() не delete', если этот объект завершен с его использованием.

Кроме того, когда parser() встречает токен StartObject или StartArray, это new 'sa std::stack объект, который никогда не будет delete' d, когда объект JSON, которому он назначен, уничтожен , Вам нужно добавить деструктор в ваш класс JSON, чтобы освободить эти std::stack объекты, а также JSON объекты, на которые они держат указатели.

Но затем вы сталкиваетесь с другой проблемой, потому что такой деструктор сломается, если filter() когда-либо вызывается, потому что он возвращает new 'd JSON объект, который содержит указатели на значения, которые принадлежат другому JSON объект. При delete 'отфильтрованном объекте JSON эти значения будут уничтожены, что приведет к повреждению исходного JSON объекта, который был отфильтрован.

Кроме того, вы нигде не принимаете во внимание исключения (а те, которые вы сами бросаете, не выдают правильно). Когда вы выделяете память, вы никак не защищаете ее, чтобы освободить ее, если возникнет непредвиденное исключение.

С учетом всего вышесказанного, попробуйте что-то более похожее на это (примечание: этот код предназначен для компиляторов с поддержкой C ++ Builder C ++ 11 - если вы используете один из «классических» пред-C ++ 11 компиляторы, вам придется соответствующим образом изменить этот код):

//---------------------------------------------------------------------------
#ifndef JSONH
#define JSONH

#include <System.Classes.hpp>
#include <System.StrUtils.hpp>

#include <System.JSON.Readers.hpp>
#include <System.JSON.Types.hpp>
#include <System.JSON.Utils.hpp>
#include <System.JSON.Writers.hpp>
#include <System.JSON.Builders.hpp>

#include <stack>
#include <fstream>
#include <utility>
#include <iostream>
#include <memory>

class JSON
{
    public: 

        // JSON types
        enum Type
        {
            Integer,
            Boolean,
            Float,
            String,
            Object,
            Array,
            Null
        };

        using UniquePtr     = std::unique_ptr<JSON>;
        using SharedPtr     = std::shared_ptr<JSON>;
        using Pair          = std::pair<UnicodeString, SharedPtr>;
        using objectStack   = std::stack<Pair>;
        using arrayStack    = std::stack<SharedPtr>;

        // Static functions
        static UniquePtr JSON::parse(const UnicodeString &str);
        static UniquePtr JSON::parser(TJsonTextReader& json_reader);

        JSON();
        explicit JSON(int val);
        explicit JSON(bool val);
        explicit JSON(float val);
        explicit JSON(const UnicodeString &val);
        explicit JSON(objectStack &val);
        explicit JSON(arrayStack &val);

        // Member attributes
        Type type;
        int               int_val;
        bool             bool_val;
        float           float_val;
        UnicodeString  string_val;
        objectStack    object_val;
        arrayStack      array_val;

        // Member functions
        UniquePtr copy() const;
        SharedPtr find(const UnicodeString &path) const;
        UniquePtr map(const UnicodeString &key) const;
        JSON * set(const UnicodeString &prop, SharedPtr value);
        JSON * push(SharedPtr value);
        UniquePtr filter(const JSON &params) const;
        SharedPtr find_by(const JSON &params) const;

        UnicodeString dump() const;
        UnicodeString stringify() const;
        int size() const;
};

//------------------------------------------------------
#endif
#pragma hdrstop

#include "JSON.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)

JSON::JSON()
{
    type = JSON::Null;
}

JSON::JSON(int val)
{
    type = JSON::Integer;
    int_val = val;
}

JSON::JSON(bool val)
{
    type = JSON::Boolean;
    bool_val = val;
}

JSON::JSON(float val)
{
    type = JSON::Float;
    float_val = val;
}

JSON::JSON(const UnicodeString &val)
{
    type = JSON::String;
    string_val = val;
}

JSON::JSON(JSON::objectStack &val)
{
    type = JSON::Object;
    object_val = val;
}

JSON::JSON(JSON::arrayStack &val)
{
    type = JSON::Array;
    array_val = val;
}

JSON:::UniquePtr JSON::parse(const UnicodeString &str)
{
    if (str.IsEmpty())
        throw Exception(_D("invalid JSON: ") + str));

    std::unique_ptr<TStringReader> string_reader(new TStringReader(str));
    std::unique_ptr<TJsonTextReader> json_reader(new TJsonTextReader(string_reader.get()));

    return JSON::parser(*json_reader);
}

JSON::UniquePtr JSON::parser(TJsonTextReader &json_reader)
{   
    switch (json_reader.TokenType)
    {
        case TJsonToken::None:
            json_reader.Read();
            return JSON::parser(json_reader);

        //INTEGER
        case TJsonToken::Integer:
            return new JSON(json_reader.Value.AsInteger());

        //FLOAT
        case TJsonToken::Float:
            return new JSON(json_reader.Value.AsExtended());

        //STRING
        case TJsonToken::String:
            return new JSON(json_reader.Value.AsString());

        //BOOLEAN
        case TJsonToken::Boolean:
            return new JSON(json_reader.Value.AsBoolean());

        // OBJECT
        case TJsonToken::StartObject:
        {
            objectStack values;
            while (json_reader.Read() && json_reader.TokenType != TJsonToken::EndObject)
            {
                UnicodeString key = json_reader.Value.AsString();
                json_reader.Read();
                JSON::SharedPtr val = JSON::parser(json_reader);
                values.push(std::make_pair(key, val));
            }
            return new JSON(values);
        }

        // ARRAY
        case TJsonToken::StartArray:
        {
            arrayStack values;
            while (json_reader.Read() && json_reader.TokenType != TJsonToken::EndArray)
            {
                JSON::SharedPtr val = JSON::parser(json_reader);
                values.push(val);
            }
            return new JSON(values);
        }

        //NULL
        case TJsonToken::Null:
        case TJsonToken::Undefined:
            return new JSON;
    }

    return nullptr;
}

JSON::SharedPtr JSON::find(const UnicodeString &path) const
{
    if ((type == JSON::Object) || (type == JSON::Array))
    {
        TStringDynArray slice = SplitString(path, _D("."));

        if (type == JSON::Object)
        {
            for (std::size_t i = 0; i < object_val.size(); ++i)
            {
                const JSON::Pair &p = json.object_val.c[i];

                if (p.first == slice[0])
                {
                    if (slice.Length > 1)
                    {
                        UnicodeString next = slice[1];
                        for (int i = 2; i < slice.Length; ++i)
                            next += ("." + slice[i]);

                        return p.second->find(next);
                    }
                    else
                        return p.second;
                }
            }
        }
        else
        {
            int i;
            if (TryStrToInt(slice[0], i))
            {
                JSON::SharedPtr &val = array_val.c[i];

                if (slice.Length > 1)
                {
                    UnicodeString next = slice[1];
                    for (i = 2; i < slice.Length; ++i)
                        next += (_D(".") + slice[i]);

                    return val->find(next);
                }
                else
                    return val;
            }
        }
    }

    return nullptr;
}

static UnicodeString stringify(const UnicodeString &string_val)
{
    return _D("\"") + string_val + _D("\""); // TODO: escape reserved characters!
}

static UnicodeString stringify(const JSON::Pair &p)
{
    return stringify(p.first) + _D(":") + p.second->stringify();
}

UnicodeString JSON::stringify() const
{
    switch (type)
    {
        //INTEGER
        case JSON::Integer:
            return int_val;

        //FLOAT
        case JSON::Float:
        {
            TFormatSettings fmt = TFormatSettings::Create();
            fmt.DecimalSeparator = _D('.');
            fmt.ThousandDecimalSeparator = _D('\0');
            return FloatToStr(float_val, fmt);
        }

        //STRING
        case JSON::String:
            return stringify(string_val);

        //BOOLEAN
        case JSON::Boolean:
            return bool_val ? _D("true") : _D("false");

        // OBJECT
        case JSON::Object:
        {
            if (!object_val.empty())
            {
                UnicodeString str = _D("{") + stringify(object_val.c[0]);

                for(std::size_t i = 1; i < object_val.size(); ++i)
                    str += (_D(", ") + stringify(object_val.c[i]));

                str += _D("}");
                return str;
            }
            else
                return _D("{}");
        }

        // ARRAY
        case JSON::Array:
        {
            if (!array_val.empty())
            {
                UnicodeString str = _D("[") + array_val.c[0]->stringify();

                for (std::size_t i = 1; i < array_val.size(); ++i)
                    str += (_D(", ") + array_val.c[i]->stringify());

                str += _D("]");
                return str;
            }
            else
                return _D("[]");
        }

        //NULL
        case JSON::Null:
            return _D("null");
    }

    return _D("");
}

UnicodeString JSON::dump() const
{
    UnicodeString d = stringify();
    return StringReplace(d, _D("\""), _D(""), TReplaceFlags() << rfReplaceAll);
}

JSON::UniquePtr JSON::map(const UnicodeString &key) const
{
    if (type != JSON::Array)
        throw Exception(_D("Not an array"));

    arrayStack values;
    for (std::size_t i = 0; i < array_val.size(); ++i)
    {
        JSON::SharedPtr val = array_val.c[i];
        if (val->type != JSON::Object)
            throw Exception(_D("Not an array of objects"));

        JSON::SharedPtr j = val->find(key);
        if (j)
            values.push(j->copy());
    }

    return new JSON(values);
}

int JSON::size() const
{
    switch (type)
    {
        case JSON::Object:
            return static_cast<int>(object_val.size());

        case JSON::Array:
            return static_cast<int>(array_val.size());

        case JSON::String:
            return string_val.Length();
    }
    return 0;
}

JSON * JSON::set(const UnicodeString &prop, JSON::SharedPtr value)
{
    if (type != JSON::Object)
        throw Exception(_D("This is not an object"));

    for (std::size_t i = 0; i < object_val.size(); ++i)
    {
        JSON::Pair &p = json.object_val.c[i];

        if (p.first == prop)
        {
            p.second = value;
            return this;
        }
    }

    object_val.push(std::make_pair(prop, value));
    return this;
}

JSON * JSON::push(JSON::SharedPtr value)
{
    if (type != JSON::Array)
        throw Exception(_D("This is not an array"));

    array_val.push(value);
    return this;
}

JSON::UniquePtr JSON::copy() const
{
    switch (type)
    {
        //INTEGER
        case JSON::Integer:
            return new JSON(int_val);

        //FLOAT
        case JSON::Float:
            return new JSON(float_val);

        //STRING
        case JSON::String:
            return new JSON(string_val);

        //BOOLEAN
        case JSON::Boolean:
            return new JSON(bool_val);

        // OBJECT
        case JSON::Object:
        {
            objectStack values;
            for (std::size_t i = 0; i < object_val.size(); ++i)
            {
                UnicodeString key = object_val.c[i].first;
                JSON::SharedPtr val = object_val.c[i].second->copy();
                values.push(std::make_pair(key, val));
            }
            return new JSON(values);
        }

        // ARRAY
        case JSON::Array:
        {
            arrayStack values;
            for (std::size_t i = 0; i < array_val.size(); ++i)
            {
                JSON::SharedPtr val = array_val.c[i]->copy();
                values.push(val);
            }
            return new JSON(values);
        }

        //NULL
        case JSON::Null:
            return new JSON;
    }

    return nullptr;
}

JSON::UniquePtr JSON::filter(const JSON &params)
{
    if (type != JSON::Array)
        throw Exception(_D("this is not an array"));

    if (params.type != JSON::Object)
        throw Exception(_D("params is not an object"));

    arrayStack values;

    for (std::size_t i = 0; i < array_val.size(); ++i)
    {
        JSON::SharedPtr &val = array_val.c[i];

        for (std::size_t it = 0; it < params.size(); ++it)
        {
            JSON::SharedPtr this_value = val->find(params.object_val.c[it].first);

            /*
            UnicodeString str_params = params.stringify();
            UnicodeString str_this = stringify();
            UnicodeString key_test = params.object_val.c[it].first;
            UnicodeString this_test = this_value->stringify();
            UnicodeString params_test = params.object_val.c[it].second->stringify();
            /*

            if (this_value)
            {
                if (this_value->stringify() == params.object_val.c[it].second->stringify())
                    values.push(val);
            }
        }
    }

    return new JSON(values);
}

JSON::SharedPtr JSON::find_by(const JSON &params)
{
    JSON::UniquePtr filtered = filter(params);
    if (filtered->size())
        return filtered->find(_D("0"));
    return nullptr;
}
0 голосов
/ 17 апреля 2019

В C / C ++ существует простое правило проектирования: тот, кто выделяет, должен освобождать.

Это действительно в 99% случаев.Если вы думаете, что вы должны разработать его иначе, подумайте еще раз.

Проверка, которая идет с этим правилом, состоит в том, что вы ДОЛЖНЫ иметь столько же new, сколько delete.В вашем примере у вас есть 8 new, 2 delete.(применяется также в C с malloc / free).

Действительно, лучший деструктор должен быть полезен.

Если у вас все еще есть утечка памяти, проверьте инструмент valgrind , который может помочь вам локализовать утечки памяти.Вы можете проверить умные указатели, чтобы избежать проблем с управлением памятью

Добро пожаловать в переполнение стека =)

...