Скачать MetaTrader 5

Удаляется счетчик ссылок в shared_ptr

Авторизуйтесь или зарегистрируйтесь, чтобы добавить комментарий
vasya-gaykin
48
vasya-gaykin 2016.03.12 13:19 

Я написал простенькую реализацию shared_ptr для управления динамически создаваемыми объектами.

В целом все работает отлично. Но. При непонятных условиях происходит незапланированное удаление счетчика ссылок, после чего возникает ошибка invalid pointer access;

Я вставлял диагностические сообщения перед оператором delete refCount, но до возникновения ошибки этот участок кода не вызывался, получается удаление происходило за пределами класса Pointer.

void Pointer::reduceCounter()
{   
    --refCount; // !!!!!!!!!! ОШИБКА ВОЗНИКАЕТ ЗДЕСЬ
    if (refCount <= 0) 
    {          
        if (CheckPointer(obj) == POINTER_DYNAMIC)
        {
            delete obj;
            obj = NULL;
        }
        Print("delete refCount"); // !!! До ошибки этот оператор не вызывается
        delete refCount;
        refCount = NULL;
    }
}

Я как смог минимизировал контекст возникновения ошибки, но что делать дальше, ума не приложу. Поведение очень странное.

Советник, который продемонстрирует условия возникновения ошибок:

#property strict

#include "Pointer_impl.mqh"

void OnStart()
{
    bool res;
    Pointer p(new Object());
    
    // normal
    res = false || p.doSomething().getFalse();
    res = p.getFalse() || p.doSomething().getFalse();
    res = true && p.doSomething().getFalse();
    res = p.getTrue() && p.doSomething().getFalse();
    
    // error
    res = true || p.doSomething().getFalse();
    res = p.getTrue() || p.doSomething().getFalse();
    res = false && p.doSomething().getFalse();
    res = p.getFalse() && p.doSomething().getFalse();
}

Смущает то, что часть практически идентичных примеров работает, часть -- нет.

К сообщению прикрепил архив, чтобы можно было все ручками пощупать, через дебаггер пропустить. Сам я зашел в тупик.

Код класса Object:

#property strict

#ifndef OBJECT_MQH
#define OBJECT_MQH

#include "Pointer.mqh"

class Object
{
    public:
        Object();
        
        Pointer doSomething() const;
        bool getTrue() const;
        bool getFalse() const;
};

#endif // OBJECT_MQH
#property strict

#include "Object.mqh"

Object::Object()
{}

Pointer Object::doSomething() const
{
    return new Object();
}

bool Object::getFalse() const
{
    return false;
}

bool Object::getTrue() const
{
    return true;
}


Код класса Pointer:

#property strict

#ifndef JSON_JPOINTER
#define JSON_JPOINTER

#include "Integer.mqh"

class Object;

class Pointer
{
    public:
        // Pointer interface
        Pointer();
        Pointer(Object* o);
        Pointer(const Pointer& rhs);        
        Pointer* operator=(const Pointer& rhs);
        
        ~Pointer();
        
        // Object interface
        Pointer doSomething() const;
        bool getTrue() const;
        bool getFalse() const;
        
    private:
        Object* obj;
        Integer* refCount;
        
    private:
        void reduceCounter();
};

#endif // JSON_JPOINTER
#property strict

#include "Pointer.mqh"

#include "Integer_impl.mqh"
#include "Object_impl.mqh"


Pointer::Pointer():
        obj(NULL), refCount(new Integer(1))
{}

Pointer::Pointer(Object* o):
        obj(o), refCount(new Integer(1))
{}

Pointer::Pointer(const Pointer& rhs):
    obj(rhs.obj), refCount(rhs.refCount)
{
    ++refCount;
}

Pointer* Pointer::operator=(const Pointer& rhs)
{    
    reduceCounter();
    
    obj = rhs.obj;
    refCount = rhs.refCount;
    ++refCount;
    
    return &this;
}

Pointer::~Pointer()
{   
    reduceCounter();
}

void Pointer::reduceCounter()
{   
    if (--refCount <= 0)
    {          
        if (CheckPointer(obj) == POINTER_DYNAMIC)
        {
            delete obj;
            obj = NULL;
        }
        delete refCount;
        refCount = NULL;
    }
}

Pointer Pointer::doSomething() const
{
    return obj.doSomething();
}

bool Pointer::getTrue() const
{
    return obj.getTrue();
}

bool Pointer::getFalse() const
{
    return obj.getFalse();
}


Код класса Integer:

#property strict

#ifndef INTEGER
#define INTEGER

class Integer
{
    public:
        Integer(int i);
        Integer(const Integer& rhs);
        
        Integer* operator=(int rhs);
        
        Integer* operator++();
        Integer* operator--();
        
        bool operator<=(int rhs) const;
        
    private:
        int val;
};

#endif // INTEGER
#property strict

#include  "Integer.mqh"

Integer::Integer(int i):
    val(i)
{}

Integer::Integer(const Integer& rhs):
    val(rhs.val)
{}

Integer* Integer::operator=(int rhs)
{
    val = rhs;
    return &this;
}

Integer* Integer::operator++()
{
    ++val;
    return &this;
}

Integer* Integer::operator--()
{
    --val;
    return &this;
}

bool Integer::operator<=(int rhs) const
{
    return val <= rhs;
}
Файлы:
Dmitry Fedoseev
43471
Dmitry Fedoseev 2016.03.12 16:19  

Жосткая школа ООП))) Даже обычная переменная типа int обернута в класс. А что, просто переменной int пользоваться религия не позволяет?

Это из тех случаев, когда вспомнишь про "Шарикову больше не наливать". 

vasya-gaykin
48
vasya-gaykin 2016.03.12 16:30  

Integer нужен по причине, что терминал не позволяет выполнить new int.

он вообще много чего не позволяет ((

Vasiliy Sokolov
22607
Vasiliy Sokolov 2016.03.12 17:12  
vasya-gaykin:

Integer нужен по причине, что терминал не позволяет выполнить new int.

он вообще много чего не позволяет ((

А зачем вообще выполнять new int!? Зачем использовать счетчик ссылок, если управление объектами по-хорошему делегируется контейнеру?  Также Вы в курсе что существует стандартный и базовый класс всех классов CObject? Зачем строить еще одну железную дорогу, если одна уже есть и хорошо работает?

Понимаете, решить Вашу проблему можно двумя путями: разобраться в счетчике ссылок, а это сделать наверное кроме Вас никто не сможет. Либо провести несколько сеансов гештальт-терапии, и наконец осознать полную ошибочность выбранного Вами подхода.

З.Ы. Укажите пожалуйста, задачу, которую требуется решить представленными здесь кодами.

Vasiliy Sokolov
22607
Vasiliy Sokolov 2016.03.12 17:24  

Сейчас чуть более внимательно присмотрелся к коду - ужасные ошибки. Такое ощущение что имеет место полное непонимание написанного кода, тем, кто его сам писал:

Pointer Object::doSomething() const
{
    return new Object();
}

Где звездочка (Pointer может быть только указателем)? Нельзя вернуть Object вместо Pointer, если Object не наследуется от Pointer.

vasya-gaykin
48
vasya-gaykin 2016.03.12 19:17  
Vasiliy Sokolov:

Сейчас чуть более внимательно присмотрелся к коду - ужасные ошибки. Такое ощущение что имеет место полное непонимание написанного кода, тем, кто его сам писал:

Где звездочка (Pointer может быть только указателем)? Нельзя вернуть Object вместо Pointer, если Object не наследуется от Pointer.

в этом месте отработает вот такой конструктор
Pointer::Pointer(Object* o):
        obj(o), refCount(new Integer(1))
{}
vasya-gaykin
48
vasya-gaykin 2016.03.12 20:02  
Vasiliy Sokolov:

А зачем вообще выполнять new int!? Зачем использовать счетчик ссылок, если управление объектами по-хорошему делегируется контейнеру?  Также Вы в курсе что существует стандартный и базовый класс всех классов CObject? Зачем строить еще одну железную дорогу, если одна уже есть и хорошо работает?

Понимаете, решить Вашу проблему можно двумя путями: разобраться в счетчике ссылок, а это сделать наверное кроме Вас никто не сможет. Либо провести несколько сеансов гештальт-терапии, и наконец осознать полную ошибочность выбранного Вами подхода.

З.Ы. Укажите пожалуйста, задачу, которую требуется решить представленными здесь кодами.

new int нужен для разделения счетчика между Pointer'ами, по другому эта задача не решается.

Pointer и есть контейнер для управления объектами Object

Решаемая задача парсинг JSON; вместо Object'a наборы классов JSON, JSONObject, JSONArray, JSONString...

vasya-gaykin
48
vasya-gaykin 2016.03.12 20:07  

Возможно из-за длинного первого поста размылась сама суть проблемы.

Прошу обратить внимание на советника:

#property strict

#include "Pointer_impl.mqh"

void OnStart()
{
    bool res;
    Pointer p(new Object());
    
    // normal
    res = false || p.doSomething().getFalse();

    // normal
    p.doSomething().getFalse();
     
    // error
    res = true || p.doSomething().getFalse();
}


идентичные вызовы, половина из которых отрабатывает нормально, половина валится с ошибкой, и получается, что ошибка не в коде подсчета ссылок.

Причем с ошибкой валятся те примеры, которые не должны выполнятся из-за ленивых вычислений логических выражений (хотя, возможно,  компилятор MT4 этими оптимизациями не заморачивается)

Алексей Тарабанов
7279
Алексей Тарабанов 2016.03.12 22:37  
vasya-gaykin:

Возможно из-за длинного первого поста размылась сама суть проблемы.

Прошу обратить внимание на советника:


идентичные вызовы, половина из которых отрабатывает нормально, половина валится с ошибкой, и получается, что ошибка не в коде подсчета ссылок.

Причем с ошибкой валятся те примеры, которые не должны выполнятся из-за ленивых вычислений логических выражений (хотя, возможно,  компилятор MT4 этими оптимизациями не заморачивается)

Ничего не смыслю в ООП, но, как я понимаю, объекты класса res имеют значение true, если некие другие объекты созданы успешно. 

Смутно догадываюсь, что  свойство p.doSomething().getFalse() характеризует отсутствие положительного исхода какого-то процесса, либо действия. назову соответствующую булеву переменную res1. 

Теперь отследим логику представленной программы:

 if( !res || !res1) OK; 

if( !res1 ) OK; 

if( res || !res1 ) !OK;

Меня бы за это линейкой по рукам хлестать не стали,- просто из бурсы выгнали бы в момент :(  

ЗЫ: Возможно, эта тема просто не входит в круг моего понимания...  

Dmitry Fedoseev
43471
Dmitry Fedoseev 2016.03.12 22:45  
vasya-gaykin:

Integer нужен по причине, что терминал не позволяет выполнить new int.

он вообще много чего не позволяет ((

Хотя бы сделайте просто класс и в нем публичное свойство, его и плюсуйте/минусуйте. Вообще подход, который привел к такой потребности изначально неверен. Выглядит все вообще, как будто запредельный сверх-экстра-супер мастер ООП кодил, даже страшно рядом стоять. Но почему супер герой задает вопросы?

Зачем создавать новый счетчик?  Счетчик чего для чего? Вот что он считает. Почему его не сделать полем какого-то класса?

Koldun Zloy
246
Koldun Zloy 2016.03.13 06:52  
vasya-gaykin:

Я написал простенькую реализацию shared_ptr для управления динамически создаваемыми объектами.

В целом все работает отлично. Но. При непонятных условиях происходит незапланированное удаление счетчика ссылок, после чего возникает ошибка invalid pointer access;

Я вставлял диагностические сообщения перед оператором delete refCount, но до возникновения ошибки этот участок кода не вызывался, получается удаление происходило за пределами класса Pointer.

Я как смог минимизировал контекст возникновения ошибки, но что делать дальше, ума не приложу. Поведение очень странное.

Советник, который продемонстрирует условия возникновения ошибок:

Смущает то, что часть практически идентичных примеров работает, часть -- нет.

К сообщению прикрепил архив, чтобы можно было все ручками пощупать, через дебаггер пропустить. Сам я зашел в тупик.

Код класса Object:


Код класса Pointer:


Код класса Integer:

В Вашем коде нет ошибок. Пишите в сервисдеск.

12
Авторизуйтесь или зарегистрируйтесь, чтобы добавить комментарий