LINUX.ORG.RU

Реализуйте класс SharedPtr

 


0

3

Здраствуйте, помогите решить задание (2-а дня плавится мозг).

Условие: Задание повышенной сложности. Реализуйте класс SharedPtr как описано ранее. Задание немного сложнее, чем кажется на первый взгляд. Уделите особое внимание «граничным случаям» - нулевой указатель, присваивание самому себе, вызов reset на нулевом SharedPtr и прочее. Hint: возможно, вам понадобится завести вспомогательную структуру. Код к заданию:

struct Number;
struct BinaryOperation;
struct FunctionCall;
struct Variable;

struct SharedPtr
{
    // реализуйте следующие методы
    //
    // explicit SharedPtr(Expression *ptr = 0)
    // ~SharedPtr()
    // SharedPtr(const SharedPtr &)
    // SharedPtr& operator=(const SharedPtr &)
    // Expression* get() const
    // void reset(Expression *ptr = 0)
    // Expression& operator*() const
    // Expression* operator->() const
};

Моя реализация (1-й день):

Expression *ptr_;
int *refs;

void clear(){
        if (!--*refs){
            delete ptr_;
            delete refs;
        }
    }

explicit SharedPtr(Expression *ptr = 0) 
	: ptr_(ptr), refs(new int(1))
{}
~SharedPtr() {
	clear();
}

SharedPtr(const SharedPtr &other)
	: ptr_(other.ptr_), refs(other.refs)
{
	++*refs;
}

SharedPtr& operator=(const SharedPtr & other) {
    if (this != &other){
        //clear();
        ptr_ = other.ptr_;
        refs    = other.refs;
        ++*refs;
    }
    return *this;
}

//SharedPtr& operator=(Expression *ptr){
//        if (ptr_ != ptr){
//            ptr_ = ptr;
//            *refs = 1;
//        }
//        return *this;
//    }

Expression* get() const {
	return ptr_;	
}

void reset(Expression *ptr = 0) {
	clear();
	ptr_=ptr;
}

Expression& operator*() const {
	return *ptr_;
}

Expression* operator->() const{
	return ptr_;
}
};

День 2-й:

explicit SharedPtr(Expression *ptr = 0) {
   this->ptr_ = ptr;
   if( ptr == 0 )
     this->reference_ = 0;
   else
     this->reference_ = new int(1);
 }


~SharedPtr() {
    if( this->reference_ != 0 )
    	this->reset();
}

SharedPtr(SharedPtr &other) {
  	if ( other.get() == this->ptr_ ) return;
  	this->ptr_ = other.get();
  	this->reference_ = other.get_reference_();
  	(*this->reference_)++;
}  

SharedPtr& operator=(SharedPtr & other) {
    if ( other.get() == this->ptr_ ) return *this;
    this->reset();
    this->ptr_ = other.get();
    this->reference_ = other.get_reference_();
    *(this->reference_) += 1;
    return *this;
}

Expression* get() const {
	return this->ptr_;
}

void reset(Expression *ptr = 0) {
    if( this->reference_ == 0 && this->ptr_ == 0 ) return;
    *(this->reference_) = *(this->reference_) - 1;
    if( *this->reference_ == 0 && this->reference_ != 0){
        delete this->ptr_;
        delete this->reference_;
    }
    this->reference_ = 0;
    this->ptr_ = 0;
}

Expression& operator*() const {
	return *(this->ptr_);
}

Expression* operator->() const{
	return this->ptr_;
}

int* get_reference_() { return this->reference_; }

private:
  Expression *ptr_;
  int * reference_;
};

Люди реализовывали без доп, структур. Пишет не правельный ответ и все тут :((

Прошу помощи...

Условие: Задание повышенной сложности. Реализуйте класс SharedPtr как описано ранее

как описано ранее

Отличная постановка задачи!

hey-propagandist
()

не правельный

извини, я не буду тебе помогать.

Progressive
()

ошибка в конструкторе копирования: this->ptr_ не будет определена в момент вызова. Думаю нужно как-то так:

this->ptr_ = 0; 
this->reference_ = 0;

if ( other.get() && other.get_reference_()) {
// если указатель не пуст
    this->ptr_ = other.get(); 
    this->reference_ = other.get_reference_();
    (*this->reference_)++; 
}
тоже с оператор =

zudwa
()
Ответ на: комментарий от zudwa

Для operator = д.б. такие варианты: 1) если this пуст и other пуст: ничего не делать 2) если this пуст и other не пуст: присвоить ссылку, инкрементировать счетчик; 3) если this не пуст и other пуст: декрементировать счетчик this, если счетчик 0 - удалить память обьекта и счетчика, после чего обнулить указатели; 4) оба не пусты, но равны: присвоить указатель из other в this, инкрементировать счетчик; 5) оба не пусты, и не равны: декрементировать счетчик this, если счетчик 0 - удалить память обьекта и счетчика, присвоить указатель из other в this, инкрементировать счетчик;

как-то так

zudwa
()
Ответ на: комментарий от hey-propagandist

По поводу описанного ранее: Первый реализованный Класс: Какой интерфейс может быть у такого класса ScopedPtr? Кроме уже известных вам операторов * и ->, деструктора и конструктора, полезными могут оказаться следующие методы:

get - возвращает указатель, сохраненный внутри ScopedPtr (например, чтобы передать его в какую-то функцию); release - забирает указатель у ScopedPtr и возвращает значение этого указателя, после вызова release ScopedPtr не должен освобождать память (например, чтобы вернуть этот указатель из функции); reset - метод заставляет ScopedPtr освободить старый указатель, а вместо него захватить новый (например, чтобы переиспользовать ScopedPtr, так как оператор присваивания запрещен).

Условие для текущего задания: Для ScopedPtr мы запретили копирование, однако, копирование можно и разрешить. Это позволит реализовать более продвинутый умный указатель - SharedPtr. SharedPtr отличается от ScopedPtr тем, что кроме хранения указателя на объект, он хранит еще и счетчик ссылок (количество объектов SharedPtr, которые хранят один и тот же указатель).

Имея такой счетчик, мы можем определить момент, когда на объект, выделенный в куче, не останется больше ссылок (когда счетчик ссылок станет равным 0), и освободить память.

Поддержка счетчика ссылок состоит из нескольких частей:

в конструкторе SharedPtr от ненулевого указателя мы инициализируем счетчик ссылок в 1 (конструктор создает первый SharedPtr, который хранит указатель) в конструкторе копирования мы увеличиваем счетчик ссылок на 1, если копируемый SharedPtr содержит ненулевой указатель (конструктор копирования создает еще один SharedPtr с указателем на тот же самый объект) в деструкторе мы уменьшаем значение счетчика на 1, если в объекте SharedPtr хранится ненулевой указатель (мы удаляем один SharedPtr, который указывает на объект в куче) оператор присваивания уменьшает счетчик ссылок левого операнда на 1, если внутри левого SharedPtr хранится ненулевой указатель, увеличивает счетчик правого SharedPtr на 1, если в правом SharedPtr хранится ненулевой указатель (обычное дело для оператора присваивания - сначала освобождаем старые ресурсы, потом выделяем новые, но при этом нужно быть особенно внимательным с присваиванием самому себе) Для класса SharedPtr могут оказаться полезными следующие методы (кроме операторов * и ->, конструктора копирования, оператора присваивания, деструктора и конструктора):

метод get, как и в случае со ScopedPtr; метод reset - аналогичен reset у ScopedPtr, но освобождает память, только если счетчик ссылок после декремента равен 0;

green_day
() автор топика
Ответ на: комментарий от hey-propagandist

Уважаемый hey-propagandist, зачем оскорблять человек которого вы не знаете? Не вижу аргументов считать вас не LoL

green_day
() автор топика

Надеюсь, там есть –10 баллов, если смарт-поинтер лажает при использовании из нескольких потоков?

ilammy ★★★
()

Это ведь онлайн-задачник? Дай ссылку, если это публичное.

crowbar
()

«Без вспомогательных структур» получится неработающее говно, поскольку нужно явно управлять временем жизни счетчика ссылок и обеспечить его адекватное изменение из разных потоков. Если есть cmpxchg16b (для x86_64) или cmpxchg8b (для x86), можно обойтись без мьютексов. Вся концепция shared pointer'а зиждется на том, что между потоками путешествует его копия, а не указатель/ссылка на SharedPtr.

#include <assert.h>
#include <pthread.h>
#include <stdexcept>

class Mutex {
    pthread_mutex_t     m_lock;
public:
    Mutex() { pthread_mutex_init(&m_lock, NULL); }
    ~Mutex() { pthread_mutex_destroy(&m_lock); }
    void lock() { pthread_mutex_lock(&m_lock); }
    void unlock() { pthread_mutex_unlock(&m_lock); }
};

class ScopedLock {
    Mutex *m_lock;
    ScopedLock() {}
    ScopedLock(const ScopedLock &l) {}
public:
    ScopedLock(Mutex &lock) : m_lock(&lock) {
        m_lock->lock();
    }
    ~ScopedLock() {
        m_lock->unlock();
    }
};

template<typename T>
class SharedPtr {
    struct SharedPtrTag {
        T               *m_ptr;
        unsigned int    m_ref;
        Mutex           m_lock;
        SharedPtrTag(T *ptr = NULL) : m_ptr(ptr), m_ref(1) { }
        ~SharedPtrTag() {
            assert(m_ref == 0);
            delete m_ptr;
        }
        void ref() {
            ScopedLock sl(m_lock);
            m_ref++;
        }
        unsigned int unref() {
            T *ptr = NULL;
            unsigned int ref;
            do {
                ScopedLock sl(m_lock);
                m_ref--;
                ref = m_ref;
                if (m_ref == 0) {
                    ptr = m_ptr;
                    m_ptr = NULL;
                }
            } while (0);
            delete ptr;
            return ref;
        }
        T *get() const {
            if (!m_ptr)
                throw std::invalid_argument("NULL pointer exception");
            return m_ptr;
        }
    };
    SharedPtrTag        *m_data;
public:
    SharedPtr(T *t = NULL) : m_data(new SharedPtrTag(t)) {}
    SharedPtr(SharedPtr<T> &p) : m_data(p.m_data) { m_data->ref(); }
    ~SharedPtr() { if (m_data->unref() == 0) delete m_data; }
    void reset(T *t) {
        if (m_data->unref() == 0)
            delete m_data;
        m_data = NULL;
        m_data = new SharedPtrTag(t);
    }
    T *get() const {
        return m_data->m_ptr;
    }
    T& operator*() const { return *m_data->get(); }
    T* operator->() const { return m_data->get(); }
};

kawaii_neko ★★★★
()
Ответ на: комментарий от kawaii_neko

не задумывался ли ты, мой юный друг, почему shared_ptr в 11-м стандарте не используется блокировки?

anonymous
()
Ответ на: комментарий от anonymous

не задумывался ли ты, мой юный друг, почему shared_ptr в 11-м стандарте не используется блокировки?

1. Потому что его сумели сделать на атомиках. И чо?
2. Внутри delete блокировок тоже нет? Что-то не верится.

Manhunt ★★★★★
()
Ответ на: комментарий от anonymous

Специально для таких как ты было упомянуто:

Если есть cmpxchg16b (для x86_64) или cmpxchg8b (для x86), можно обойтись без мьютексов

Не говоря о том, что операции настолько короткие, что простого spinlock'а хватит за глаза.

kawaii_neko ★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.