On 14.12.2016 19:13, bitrex wrote:
> I wrote a little memory pool allocator implementation that I hope to use
> with a couple embedded projects that use processors without an MMU:
> Cortex M4, etc.
>
> It's working pretty well with the STL containers like std::vector, but
> I'd like to have the ability to return managed standalone pointers to
> the objects in the pool that automatically de-allocate the objects when
> they go out of scope.
>
> I've read that correct implementation of smart pointers is a difficult
> thing for a C++ newbie to get right. I've looked over the code for
> std::unique_ptr and std::shared_ptr in the STL for C++11, and indeed
> they appear fairly complex, with a lot of excess #ifdefs for conditional
> compilation for architecture features that don't seem to be really
> relevant for my application.
If you can use STL in your project, then it looks like you are finished.
Just use std::allocate_shared(your_allocator(), ...) for creating your
objects.
> I'm wondering if anyone could provide a reference for an example
> implementation of smart pointers that are known to be semantically
> correct, but would be more suitable for my narrow application.
If you need to create a new smartpointer class from scratch, then this
is not so hard (certainly simpler than creating a threaded binary tree,
discussed else-thread).
Do you need weak pointers? Do you need thread-safe refcounting? Do you
need const-correctness? Do you need automatic conversions from derived
class smartpointers to base class smartpointers? Do you need to support
any other classes than your own?
If answer to all of these questions is no, then the smart pointer class
becomes pretty trivial. You can use "internal reference counting",
meaning that the reference counter is placed directly inside your
objects (in a common base class). This makes things a bit simpler. The
smartpointer class itself is straightforward, one just needs to take
care to define *all* needed operations and get the self-assignment and
cycle breaking assignments correct.
Example:
#include <cstddef>
// Base class for refcounted objects.
class RefCountedBase {
int refcount_;
public:
RefCountedBase(): refcount_(0) {}
RefCountedBase(const RefCountedBase& b): refcount_(0) {}
RefCountedBase& operator=(const RefCountedBase& b) { return *this; }
RefCountedBase& operator=(RefCountedBase&& b) { return *this; }
virtual ~RefCountedBase() {}
void Capture() {
++refcount_;
}
void Release() {
if (--refcount_==0) {
// destroy the object
delete this;
}
}
};
template<typename T>
class SmartPointer {
T* p_;
public:
SmartPointer(): p_(nullptr) {}
SmartPointer(std::nullptr_t): p_(nullptr) {}
explicit SmartPointer(const T* p): p_(const_cast<T*>(p)) {
if (p_) {
p_->Capture();
}
}
SmartPointer(const SmartPointer& b): p_(b.p_) {
if (p_) {
p_->Capture();
}
}
SmartPointer(SmartPointer&& b): p_(b.p_) {
b.p_ = nullptr;
}
~SmartPointer() {
if (p_) {
p_->Release();
}
}
const SmartPointer& operator=(const SmartPointer& b) {
// Support self assignment
if (b.p_) {
b.p_->Capture();
}
// Support cycle breaking
T* q = p_;
p_ = b.p_;
if (q) {
q->Release();
}
return *this;
}
const SmartPointer& operator=(SmartPointer&& b) {
// Detect self assignment
if (p_!=b.p_) {
// Support cycle breaking
T* q = p_;
p_ = b.p_;
b.p_ = nullptr;
if (q) {
q->Release();
}
}
return *this;
}
T& operator*() const {
return *p_;
}
T* operator->() const {
return p_;
}
T* get() const {
return p_;
}
bool operator<(const SmartPointer& b) const {
return p_<b.p_;
}
explicit operator bool() const { return p_!=nullptr; }
};
class A;
typedef SmartPointer<A> APtr;
class A: public RefCountedBase {
};
#include <vector>
int main() {
APtr a1(new A());
std::vector<APtr> test1(100, a1);
test1.push_back(APtr(new A()));
test1.push_back(APtr(new A()));
a1 = nullptr;
test1.clear();
// all A-s now deleted, however many there were.
}