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



我有一个内存泄漏,我追踪到这个JSON类。

我关心在使用后删除所有可能的 instace,但我认为删除运算符无法删除"object_val"和"array_val"的子结构。

这有什么意义吗?如何"递归"删除 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::p arse(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;
}
}

您的代码中有很多泄漏。

parser()方法返回JSON&引用,而不是JSON*指针(如parse()方法)。 您通过new分配一个新的JSON对象,然后通过取消引用指针将其返回给调用方,然后调用方将该对象复制到另一个分配的JSON对象中(但您没有实现正确的复制赋值运算符!),并且不会delete原始对象。 你在parse()parser()内部都犯了很多次错误。

您的find_by()方法中也同样泄漏了内存。filter()方法返回指向newJSON对象的JSON*指针,但使用该对象时find_by()不会delete该对象。

此外,当parser()遇到StartObjectStartArray令牌时,它new是一个std::stack对象,当它所分配到的JSON对象被销毁时,它永远不会delete。 您需要向JSON类添加一个析构函数,以释放这些std::stack对象以及它们包含指针的JSON对象。

但是你会遇到另一个问题,因为如果调用filter()这样的析构函数就会中断,因为它返回一个newJSON对象,其中包含指向另一个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('');
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;
}

C/C++ 中有一个简单的设计规则:分配的人必须解除分配。

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

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

事实上,更好的析构函数应该是有用的。

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

欢迎使用堆栈溢出 =)

相关内容

最新更新