Implementation of unique_ptr in c++98
Introduced in c++11, unique_ptr
is a smart pointer, I already have a post about what are smart pointers, in that post I sorted pointers by order of usefulness, being unique_ptr
the most useful followed by raw pointers.
We have raw pointers in c++98, and I don't need shared_ptr
s, they are not that useful compared to other pointers, and I can make a workaround for those, but unique_ptr
s are really useful, more than raw pointers, so I decided to implement them in c++98, so I can use unique_ptr
s in my c++98 projects.
Note: I know we have auto_ptr
in c++98, but unique_ptr
has more features, features that I thing are useful and worth implementing, like the deleter function template parameter, the array template specialization, and some methods.
Contents
Implementation
Based on GNU c++11 unique_ptr
, I cleaned all the c++11 syntax, removed c++11 stuff like the move operators, and using the assing operators instead (which are deleted in the c++11 implementation of course), and I know that's no good, but it's the c++98 way.
This class also depends on my nullptr implementation in c++98, but with some changes, I declared the class type nullptr_t
inside a namespace nstd
(No Standard), same namespace I used for unique_ptr
, so they are not in the global scope, just like std::nullptr_t
and std::unique_ptr<>
are in the std
scope, nstd::nullptr_t
and nstd::unique_ptr<>
are on their own scope.
But -of course- nullptr
instance of nullptr_t
is on the global scope, so you can use it just be typing nullptr
, the same way you would use the c++11 keyword nullptr
.
unique_ptr
unique_ptr.h
#ifndef NSTD_UNIQUEPTR_H_
#define NSTD_UNIQUEPTR_H_
#include <algorithm> // std::swap
#include "nullptr_t.h"
namespace nstd {
template <typename Tp>
struct default_delete {
default_delete() {}
~default_delete() {}
void operator()(Tp* ptr) const {
//assert(sizeof(Tp) > 0); // this should be a static_assert in c++11
delete ptr;
}
};
template <typename Tp>
struct default_delete<Tp[]> {
default_delete() {}
void operator()(Tp* ptr) const {
//assert(sizeof(Tp) > 0); // this should be a static_assert in c++11
delete[] ptr;
}
};
template <typename Tp, typename Dp = default_delete<Tp> >
class unique_ptr {
public:
typedef Tp* pointer;
typedef Tp element_type;
typedef Dp deleter_type;
// constructors
unique_ptr() {}
explicit unique_ptr(pointer p)
: p_(p), d_(deleter_type()) {}
unique_ptr(pointer p, deleter_type d)
: p_(p), d_(d) {}
unique_ptr(nullptr_t)
: p_(nullptr), d_(nullptr) {}
// move constructors
// we are in c++98 so they are assign, but will behave like move ones
unique_ptr(unique_ptr& u) : p_(u.release(), d_(u.get_deleter())) {}
// destructor
~unique_ptr() {
reset();
}
// movement
// we are in c++98 so they are assigment, but will behave like move ones
unique_ptr& operator=(unique_ptr& u) {
reset(u.release());
get_deleter() = u.get_deleter();
return *this;
}
unique_ptr& operator=(nullptr_t) {
reset();
return *this;
}
// observers
//typename element_type::type& operator*() const {
element_type& operator*() const {
return *get();
}
pointer operator->() const {
return get();
}
pointer get() const {
return p_;
}
deleter_type& get_deleter() {
return d_;
}
const deleter_type& get_deleter() const {
return d_;
}
operator bool() const {
return get() == pointer() ? false : true;
}
// modifiers
pointer release() {
pointer p = get();
p_ = pointer();
return p;
}
void reset(pointer p = pointer()) {
std::swap(p_, p);
if (p != pointer()) {
get_deleter()(p);
}
}
void swap(unique_ptr& u) {
std::swap(p_, u.p_);
std::swap(d_, u.d_);
}
private:
pointer p_;
deleter_type d_;
};
template <typename Tp, typename Dp>
class unique_ptr<Tp[], Dp> {
public:
typedef Tp* pointer;
typedef Tp element_type;
typedef Dp deleter_type;
// constructors
unique_ptr() {}
explicit unique_ptr(pointer p)
: p_(p), d_(deleter_type()) {}
unique_ptr(pointer p, deleter_type d)
: p_(p), d_(d) {}
unique_ptr(nullptr_t)
: p_(nullptr), d_(nullptr) {}
// move constructors
// we are in c++98 so they are assign, but will behave like move ones
unique_ptr(unique_ptr& u) : p_(u.release(), d_(u.get_deleter())) {}
// destructor
~unique_ptr() {
reset();
}
// movement
// we are in c++98 so they are assigment, but will behave like move ones
unique_ptr& operator=(unique_ptr& u) {
reset(u.release());
get_deleter() = u.get_deleter();
return *this;
}
unique_ptr& operator=(nullptr_t) {
reset();
return *this;
}
// observers
//typename element_type::type& operator[](size_t i) const {
element_type& operator[](size_t i) const {
return get()[i];
}
pointer get() const {
return p_;
}
deleter_type& get_deleter() {
return d_;
}
const deleter_type& get_deleter() const {
return d_;
}
operator bool() const {
return get() == pointer() ? false : true;
}
// modifiers
pointer release() {
pointer p = get();
p_ = pointer();
return p;
}
void reset(pointer p = pointer()) {
std::swap(p_, p);
if (p != nullptr) {
get_deleter()(p);
}
}
void reset(nullptr_t) {
pointer p = get();
p_ = pointer();
if (p != nullptr) {
get_deleter()(p);
}
}
void swap(unique_ptr& u) {
std::swap(p_, u.p_);
std::swap(d_, u.d_);
}
private:
pointer p_;
deleter_type d_;
};
template <typename Tp, typename Dp>
inline void swap(unique_ptr<Tp, Dp>& x, unique_ptr<Tp, Dp>& y) {
x.swap(y);
}
template <typename Tp, typename Dp, typename Up, typename Ep>
inline bool operator==(const unique_ptr<Tp, Dp>& x, const unique_ptr<Up, Ep>& y) {
return x.get() == y.get();
}
template <typename Tp, typename Dp>
inline bool operator==(const unique_ptr<Tp, Dp>& x, nullptr_t) {
return x.get() == nullptr;
}
template <typename Tp, typename Dp>
inline bool operator==(nullptr_t, const unique_ptr<Tp, Dp>& y) {
return nullptr == y.get();
}
template <typename Tp, typename Dp, typename Up, typename Ep>
inline bool operator!=(const unique_ptr<Tp, Dp>& x, const unique_ptr<Up, Ep>& y) {
return !(x.get() == y.get());
}
template <typename Tp, typename Dp>
inline bool operator!=(const unique_ptr<Tp, Dp>& x, nullptr_t) {
return x.get() != nullptr;
}
template <typename Tp, typename Dp>
inline bool operator!=(nullptr_t, const unique_ptr<Tp, Dp>& y) {
return nullptr != y.get();
}
template <typename Tp, typename Dp, typename Up, typename Ep>
inline bool operator<(const unique_ptr<Tp, Dp>& x, const unique_ptr<Up, Ep>& y) {
return x.get() < y.get();
}
template <typename Tp, typename Dp, typename Up, typename Ep>
inline bool operator<=(const unique_ptr<Tp, Dp>& x, const unique_ptr<Up, Ep>& y) {
return !(y.get() < x.get());
}
template <typename Tp, typename Dp, typename Up, typename Ep>
inline bool operator>(const unique_ptr<Tp, Dp>& x, const unique_ptr<Up, Ep>& y) {
return y.get() < x.get();
}
template <typename Tp, typename Dp, typename Up, typename Ep>
inline bool operator>=(const unique_ptr<Tp, Dp>& x, const unique_ptr<Up, Ep>& y) {
return !(x.get() < y.get());
}
} // namespace nstd
#endif // NSTD_UNIQUEPTR_H_
nullptr_t
nullptr_t.h
#ifndef NSTD_NULLPTRT_H_
#define NSTD_NULLPTRT_H_
namespace nstd {
class nullptr_t {
public:
template <class T>
operator T*() const {
return 0;
}
template <class C, class T>
operator T C::*() const {
return 0;
}
private:
void operator&() const;
};
} // namespace nstd
const nstd::nullptr_t nullptr = {};
#endif // NSTD_NULLPTRT_H_