Общая реализация указатель


Прежде чем вы скажете что-нибудь: из-за ограничений проекта, мы не можем использовать импульс и мы не можем использовать C++11 (но; возможно, это изменит некоторые день).

Тот факт, что я не смог использовать смарт-указатели было нытье на меня в течение некоторого времени, поэтому я решил испытать свою реализацию, и он оказался удивительно прост. Итак, я бы хотел взглянуть на него. Кстати. Я решил использовать связанный список вместо Реф-счетчик, чтобы избежать новыхИнг счетчики.

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


Вот мой SharedPtr.ч:

#ifndef __NET_UTIL_SHARED_PTR_H__
#define __NET_UTIL_SHARED_PTR_H__

#include <stddef.h>

// since we cannot use boost nor C++11, nor TR1, we have to reinvent the wheel, sigh
namespace net {
  namespace util {
    class SharedPtrBase {
      private:
        mutable SharedPtrBase const* left;
        mutable SharedPtrBase const* right;
      protected:
        SharedPtrBase();
        SharedPtrBase(SharedPtrBase const& from);
        ~SharedPtrBase();
        void leave() const; // Note: leaving is idempotent
        void join(SharedPtrBase const* where) const;
        void assertWrapper(bool) const;
      public:
        bool isSingleton() const;
    };

    template <typename T> struct Deallocator {
      private:
        bool doDelete; // not const to be def. assignable
      public:
        // Implicit Constructor on purpose!
        Deallocator(bool doDelete = true) : doDelete(doDelete) {}
        bool willDelete() const {
          return doDelete;
        }
        void operator()(T* t) const {
          if (doDelete)
            delete t;
        }
    };

    template <typename T, typename Delete = Deallocator<T> >
    class SharedPtr : private SharedPtrBase {
      private:
        Delete del;
        T* ptr;
        void drop() {
          if (ptr && isSingleton()) {
            del(ptr);
            ptr = NULL;
          }
          leave();
        }
      public:
        // SharedPtr(p,false) will not delete the pointer! Useful for Stackobjects!
        explicit SharedPtr(T* ptr = NULL, Delete del = Delete())
          : SharedPtrBase(), del(del), ptr(ptr) {
        }
        SharedPtr(SharedPtr const& from)
          : SharedPtrBase(from), del(from.del), ptr(from.ptr) {
        }
        ~SharedPtr() {
          drop();
        }
        SharedPtr& operator=(SharedPtr const& from) {
          if (&from != this) {
            drop();
            del = from.del;
            ptr = from.ptr;
            join(&from);
          }
          return *this;
        }
        bool operator==(SharedPtr const& with) const {
          return ptr == with.ptr;
        }
        bool operator==(T* with) const {
          return ptr == with;
        }
        bool operator<(SharedPtr const& with) const {
          return ptr < with.ptr;
        }
        bool operator<(T* with) const {
          return ptr < with;
        }
        T& operator*() const {
          return *operator->();
        }
        T* operator->() const {
          assertWrapper(ptr);
          return ptr;
        }
        //T* release() {
        //  leave();
        //  T* const p = ptr;
        //  ptr = NULL;
        //  return p;
        //}
    };
  }
}

#endif

И мой SharedPtr.cpp:

#include "util/SharedPtr.h"

#include <assert.h>

namespace net {
  namespace util {
    SharedPtrBase::SharedPtrBase()
      : left(this), right(this) {
    }
    SharedPtrBase::SharedPtrBase(SharedPtrBase const& from)
      : left(this), right(this) {
      join(&from);
    }
    SharedPtrBase::~SharedPtrBase() {
      leave();
    }
    // Note: leaving is idempotent.
    // Try as much as you like, you will never leave yourself.
    void SharedPtrBase::leave() const {
      left->right = right;
      right->left = left;
    }
    void SharedPtrBase::join(SharedPtrBase const* where) const {
      assert(where);
      leave();
      assert(where->left && where->right);
      left = where->left;
      left->right = this;
      right = where;
      right->left = this;
    }
    void SharedPtrBase::assertWrapper(bool condition) const {
      assert(condition);
    }
    bool SharedPtrBase::isSingleton() const {
      return left == this;
    }
  }
}


Комментарии
1 ответ

ОК. Давайте посмотрим..

void SharedPtrBase::leave() const {
left->right = right;
right->left = left;
}

Это удалит вас из списка, но не сбрасывает свои левые и правые указатели. Это нормально, когда вы называете его из деструктора() или Join(). Но насчет выхода()?

Давайте посмотрим на релиз().

    T* release() {
leave();
T* const p = ptr;
ptr = NULL;
return p;
}

Вы только что выпустили указатель, так что другие функции могут использовать его (и потенциально бесплатно это). Но все остальные члены в кольце еще есть (указатель). Что вы должны сделать, это установить указатель на null во всех членов в кольце.

Я только что потратил две минуты и нашел две ошибки. Реализуя общий указатель не так проста, как вы думаете. Им потребовалось некоторое время, чтобы получить повышение версия корректно работает во всех ситуациях. Так что это хороший проект реализации его благополучно не так проста, как вы думаете.

База SharedPtrBase используется для реализации базового кольца. Но теперь пользователи могут использовать его в качестве базового класса указатель:

SharedPtrBase*  x = new SharedPtr<int>();

delete x; // Undefined behavior (destructor is not virtual).

Да я знаю, что это глупо. Но дело-люди попадают в странные ситуации, и это может случиться. Это ваша ответственность, чтобы предотвратить ваш класс был неправильно использован. Пара решений, сделать ее в частном порядке по наследству (как вы сделали), или метод, который я хотел использовать это, чтобы сделать членом (а не родитель) контролировать кольцо. Затем ваш класс не может быть использовано (и вам не нужно сделать деструктор виртуальным).

Я лично (но это личное мнение) не нравится использование указателя в присоединиться:

void join(SharedPtrBase const* where) const;

Делая ссылку вам не надо проверить, что там, где имеет значение null и всегда должен быть использован с известного объекта на другом конце (где). Я бы просто сделал эту ссылку, как это неявно передает это требование к пользователю и, таким образом, снижает вероятность злоупотреблений.

4
ответ дан 26 ноября 2011 в 11:11 Источник Поделиться