Можно ли создать простой умный указатель базового класса без шаблонов? - PullRequest
0 голосов
/ 08 января 2012

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

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

Ответы [ 4 ]

2 голосов
/ 08 января 2012

Проблема в классе вашего указателя-члена.

В таких языках, как C #, это возможно, поскольку существует "object" класс, из которого происходит любой другой класс.В этом случае вы потеряете безопасность типов.

Однако в C ++ такой вещи нет, поэтому у вас есть 2 варианта:

  1. Использование void * и reinterpet_cast - очень некрасиво, действительно небезопасно из-за безопасности типов.
  2. Написание тонн перегрузок для вашего класса - для каждого класса.Невозможно, потому что это не будет работать с новыми классами.

Таким образом, лучший способ - использовать шаблоны.
Кстати, идея template настолько хороша, что C # начал использовать его после некоторой версии.

1 голос
/ 08 января 2012

Я не уверен, почему

#include <iostream>

class void_scoped_ptr {
public:
  virtual ~void_scoped_ptr() = 0;
  void operator=(void_scoped_ptr const&) = delete;

  explicit operator bool() const { return ptr != 0; }

protected:
  void_scoped_ptr(void* p) : ptr(p) {};

  void* ptr;
};

void_scoped_ptr::~void_scoped_ptr() {}

#define MAKE_DERIVED_SCOPED_BASE(T, NAME, DELETE, DEREF)                    \
class NAME : void_scoped_ptr {                                              \
public:                                                                     \
  typedef T element_type;                                                   \
  typedef element_type* ptr_type;                                           \
  typedef element_type const* const_ptr_type;                               \
                                                                            \
  NAME(ptr_type p) : void_scoped_ptr(p) {}                                  \
  ~ NAME() { DELETE cast(); }                                               \
  void reset(ptr_type p) {                                                  \
    DELETE cast();                                                          \
    ptr = p;                                                                \
  }                                                                         \
                                                                            \
  DEREF                                                                     \
  element_type& operator*() { return *cast(); }                             \
  element_type const& operator*() const { return *cast(); }                 \
                                                                            \
protected:                                                                  \
  ptr_type cast() { return static_cast<ptr_type>(ptr); }                    \
  const_ptr_type cast() const { return static_cast<ptr_type>(ptr); }        \
}

#define MAKE_DERIVED_SCOPED_PTR(T)                                          \
  MAKE_DERIVED_SCOPED_BASE(T, T ## _scoped_ptr, delete,                     \
    ptr_type operator->() { return cast(); }                                \
    const_ptr_type operator->() const { return cast(); }                    \
  )
#define MAKE_DERIVED_SCOPED_ARRAY(T)                                        \
  MAKE_DERIVED_SCOPED_BASE(T, T ## _scoped_array, delete [],                \
    element_type& operator[](size_t i) { return cast()[i]; }                \
    element_type const& operator[](size_t i) const { return cast()[i]; }    \
  )

struct TestClass {
  TestClass() { std::cout << "construct\n"; }
  ~TestClass() { std::cout << "destruct\n"; }

  void f() { std::cout << "f()\n"; }
};

typedef MAKE_DERIVED_SCOPED_PTR(TestClass) test_ptr;
typedef MAKE_DERIVED_SCOPED_ARRAY(TestClass) test_array;

int main() {
  {
    test_ptr p(new TestClass);
    p->f();
  }

  {
    test_array a(new TestClass[3]);
    a[2].f();
  }
}
1 голос
/ 08 января 2012

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

1 голос
/ 08 января 2012

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

Если вы планируете написать общий класс менеджера ресурсов, тогда вам нужен шаблонный класс.

...