3

For educational purposes, I was earlier today implementing a wrapper class, defined below (this is taken from a book):

#ifndef WRAPPER_H
#define WRAPPER_H

template<class T>
class Wrapper
{
public:
  Wrapper()
  { dataPtr = 0; }

  Wrapper(const T& inner)
  {
    dataPtr = inner.clone();
  }

  Wrapper(const Wrapper<T> &original)
  {
    if (original.dataPtr != 0)
      dataPtr = original.dataPtr->clone();
    else
      dataPtr = 0;
  }

  Wrapper &operator =(const Wrapper<T> &original)
  {
    if (this != &original)
    {
        if (dataPtr != 0)
          delete dataPtr;

        dataPtr = (original.dataPtr !=0) ? original.dataPtr->clone() : 0;
    }
    return *this;
  }

  ~Wrapper()
  {
    if (dataPtr != 0)
      delete dataPtr;
  }

  T &operator*()
  {
    return *dataPtr;
  }

  const T&operator*() const
  {
    return *dataPtr;
  }

  T *operator->()
  {
    return dataPtr;
  }

  const T * const operator->() const
  {
    return dataPtr;
  }
private:
  T *dataPtr;
};

#endif

The main idea is to act as a pointer, with the additional advantage of taking care of memory management, copy constructor, destructor and assignment operator. It wraps classes that have a clone method: they return a pointer to a copy of themselves (not to themselves, to a fresh copy made with new Class(*this)).

In some ways it seems to be like a unique_ptr, because the object wrapped is only accessible through this wrapper. There is a difference, though, which is what my question is about. In this wrapper class, there is a constructor defined by accepting a reference to an object of the class it wraps (the first constructor in the code above).

This is very convenient. Let's say we have classes A and B, and the constructor for B takes a reference to a Wrapper< A >. Then I can construct an object B with another object A:

A object1;
B object2(A);

This is because object2 is used to construct a Wrapper< A > (then passed to the constructor of B) using the aforementioned Wrapper constructor.

Is it possible to do this with any of the smart pointers in std::memory? My main goal here is educational, but in practice I don't want to reinvent the wheel.

Rama
  • 3,222
  • 2
  • 11
  • 26
dbluesk
  • 265
  • 3
  • 10

3 Answers3

6

A smart pointer is intended to provide ownership semantics, which can be categorized (and are with the available c++ standard implementations):

  • unique Ownership always is transferred when passed around
  • shared There's no single owner, the smart pointer counts references, and deceases if these fall to 0
  • weak A dependent pointer, but offering the ability to check if the referred pointer is still valid

That's quite different from your wrapper implementation.

πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
1

yes all of that is possible.. and for reference.. and because once I also implemented something similar.. (for educational purposes also).. I can share the code I made for a smartpointer, with reference counting.. meaning you can create as many copies of it as you want, that when the last copy is destroyed it will delete the object

#ifndef UberPointer
#define UberPointer UPointer

template <class UClass> class UPointer
{
private:
    struct UPointerRef
    {
        UClass* pObject;
        int _nCount;
        UPointerRef(UClass* pRef){_nCount=0;pObject = pRef;}
        ~UPointerRef(){if(pObject)delete pObject;}
        void AddRef(void){++_nCount;}
        void RemoveRef(void){if (--_nCount <= 0){delete this;}}
    };
    UPointerRef* _pRef;

public:
    UPointer()
    {
        _pRef = new UPointerRef(0x0);
        _pRef->AddRef();
    }
    UPointer(UClass* pPointer)
    {
        _pRef = new UPointerRef(pPointer);
        _pRef->AddRef();
    }
    UPointer(UPointer<UClass> &oPointer)
    {
        _pRef = oPointer._pRef;
        _pRef->AddRef();
    }
    ~UPointer(void)
    {
        _pRef->RemoveRef();
    }
    UClass* GetObject()
    {
        ASSERT(_pRef->pObject);
        return _pRef->pObject;
    }
    operator UClass*(void)
    {
        ASSERT(_pRef->pObject);
        return _pRef->pObject;
    }
    UClass& operator*(void)
    {
        ASSERT(_pRef->pObject);
        return *(_pRef->pObject);
    }
    UClass* operator->(void)
    {
        ASSERT(_pRef->pObject);
        return (_pRef->pObject);
    }
    UPointer& operator=(UPointer<UClass> &oPointer)
    {
        _pRef->RemoveRef();
        _pRef = oPointer._pRef;
        _pRef->AddRef();
        return *this;
    }
    UPointer& operator=(UClass* pPointer)
    {
        _pRef->RemoveRef();
        _pRef = new UPointerRef(pPointer);
        _pRef->AddRef();
        return *this;
    }
    bool operator==(UClass* pPointer)
    {
        return _pRef->pObject == pPointer;
    }
    bool operator!=(UClass* pPointer)
    {
        return _pRef->pObject != pPointer;
    }
    bool operator !(void)
    {
        return (_pRef->pObject == 0x0);
    }
    operator bool(void)
    {
        return (_pRef->pObject != 0x0);
    }
};
#endif
CaldasGSM
  • 3,032
  • 16
  • 26
  • 1
    Doesn't look like you're violating the rules, but going to drop this here anyway: [What are the rules about using an underscore in a C++ identifier?](http://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-c-identifier) – user4581301 Feb 02 '17 at 19:56
  • yeah.. through the years I kind of started to develop my own kind of notation.. is not c++ standart.. but is cross language usable.. so I use always the same notation when having to change between languages in the same project.. (C#/javascript/SQL).. it reduces the strain of adaptation every time you move.. – CaldasGSM Feb 02 '17 at 21:17
  • Naming conventions are a good thing. I use a lot of prefixes and suffixes as mnemonics and hints. The worry with the underscore is you could accidentally use a name that's owned by the standard library implementation and get compiler errors or really weird runtime results. – user4581301 Feb 02 '17 at 22:11
1
A object1;
B object2(A);

Is it possible to do this with any of the smart pointers in std::memory?

With standard smart pointers you are not going to get deep copy semantics. You will either have shallow copy semantics (with std::shared_ptr) or a move semantics (with std::unique_ptr). However, nothing prevents you from creating a clone() method in your class that returns a smart pointer. That way you can have your deep copy when you need one, while still benefiting from ownership semantics that comes with smart pointers.

Joseph Artsimovich
  • 1,499
  • 10
  • 13