这个递归 class 需要自定义析构函数?

This recursive class needs a custom destructor?

我发现了内存泄漏问题 JSON class。

我已经很关心在使用后删除所有可能的实例,但我认为删除运算符不能删除 'object_val' 和 'array_val' 的子结构。

这有意义吗?我怎样才能'recursevly'删除de结构?

//---------------------------------------------------------------------------
#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)).

更新: 使用以下析构函数解决:

        ~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;
            }
        }

C/C++中有一个简单的设计规则:分配者必须释放。

这在 99% 的情况下都是有效的。如果您认为您应该以其他方式设计它,请再考虑一下。

与该规则相符的检查是您必须拥有与 delete 一样多的 new。在您的示例中,您有 8 new、2 delete。 (它也适用于 malloc/free 的 C 语言)。

确实,更好的析构函数应该有用。

如果您仍然有内存泄漏,请查看 valgrind 工具,它可以帮助您定位内存泄漏。您可以检查智能指针以避免管理内存的一些麻烦

欢迎来到 Stack Overflow =)

您的代码中存在许多漏洞。

您的 parser() 方法 returns 一个 JSON& 引用而不是 JSON* 指针(就像您的 parse() 方法一样)。您正在通过 new 分配一个新的 JSON 对象,然后通过取消引用指针将其返回给调用者,然后调用者 将该对象复制 another 分配了 JSON 对象(但你没有实现正确的复制赋值运算符!)并且没有 delete 原始对象。你在 parse()parser().

中多次犯了这个错误

您的 find_by() 方法中也有类似的内存泄漏。您的 filter() 方法 returns 一个指向 newJSON 对象的 JSON* 指针,但是 find_by() 不是 delete 的使用完该对象后。

此外,当 parser() 遇到 StartObjectStartArray 标记时,它 new 是一个永远不会 deletestd::stack 对象'd 当分配给它的 JSON 对象被销毁时。您需要向 JSON class 添加析构函数以释放那些 std::stack 对象,以及它们持有指向的 JSON 对象。

但是你 运行 陷入另一个问题,因为如果调用 filter() 这样的析构函数将会中断,因为它 returns a new' JSON 对象,它包含指向另一个 JSON 对象所拥有的值的指针。当 delete'ing 过滤后的 JSON 对象时,这些值将被破坏,从而破坏正在过滤的原始 JSON 对象。

此外,您没有在任何地方考虑异常(并且您抛出的异常没有被正确抛出)。当您分配内存时,您不会以任何方式保护它,以便在抛出意外异常时可以释放它。

综上所述,尝试更像这样的东西(注意:此代码适用于 C++Builder 的支持 C++11 的编译器 - 如果您使用的是 "classic" 预编译器之一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('[=11=]');
            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;
}