#include <iostream>
#include <memory>
#include <type_traits>
#include <cstddef>
//Public API
class Object;
class ObjectReferencePrivate;
class ObjectReference {
friend class ObjectReferencePrivate;
private:
ObjectReference() = default;
void* operator new(std::size_t size);
public:
Object & object() const;
static void operator delete (void *p);
};
class Object {
protected:
Object() = default;
public:
void printHello() const;
std::unique_ptr<ObjectReference> makeReference();
virtual ~Object() = default;
};
std::unique_ptr<ObjectReference> makeObject();
// Implementation
static_assert(std::is_trivially_destructible<ObjectReference>::value, "ObjectReference must be trivially destructible");
class ObjectImpl: public Object {
public:
ObjectImpl() = default;
std::string mHelloString = "Hello!";
};
class ObjectReferencePrivate {
friend class ObjectReference;
private:
ObjectReferencePrivate() {
new (&object) ObjectImpl;
}
public:
static std::unique_ptr<ObjectReference> makeReference() {
return std::unique_ptr<ObjectReference>(new ObjectReference);
}
~ObjectReferencePrivate() {
reinterpret_cast<ObjectImpl *>(&object)->~ObjectImpl();
}
int referenceCounter = 1;
typename std::aligned_storage<sizeof(ObjectReference), alignof(ObjectReference)>::type q;
typename std::aligned_storage<sizeof(ObjectImpl), alignof(ObjectImpl)>::type object;
};
static_assert(std::is_standard_layout<ObjectReferencePrivate>::value, "ObjectReferencePrivate must be standard layout");
Object & ObjectReference::object() const {
const unsigned char * const_d_ptr = reinterpret_cast<const unsigned char *>(this) - offsetof(ObjectReferencePrivate, q);
unsigned char * d_ptr = const_cast<unsigned char *>(const_d_ptr);
ObjectReferencePrivate *d = reinterpret_cast<ObjectReferencePrivate *>(d_ptr);
return *reinterpret_cast<ObjectImpl *>(&d->object);
}
void* ObjectReference::operator new(std::size_t) {
return &(new ObjectReferencePrivate)->q;
}
void ObjectReference::operator delete (void *p) {
unsigned char * d_ptr = reinterpret_cast<unsigned char *>(p) - offsetof(ObjectReferencePrivate, q);
ObjectReferencePrivate *d = reinterpret_cast<ObjectReferencePrivate *>(d_ptr);
std::cout << "Attempt to delete ObjectReference. referenceCounter is " << d->referenceCounter << "." << std::endl;
if (--d->referenceCounter)
return;
std::cout << "Actualy deleting ObjectReference and object." << std::endl;
delete d;
}
void Object::printHello() const {
const std::string & helloString = static_cast<const ObjectImpl *>(this)->mHelloString;
std::cout << helloString << std::endl;
}
std::unique_ptr<ObjectReference> Object::makeReference() {
unsigned char * d_ptr = reinterpret_cast<unsigned char *>(this) - offsetof(ObjectReferencePrivate, object);
ObjectReferencePrivate *d = reinterpret_cast<ObjectReferencePrivate *>(d_ptr);
++d->referenceCounter;
return std::unique_ptr<ObjectReference>(reinterpret_cast<ObjectReference *>(&d->q));
}
std::unique_ptr<ObjectReference> makeObject() {
return ObjectReferencePrivate::makeReference();
}
//Public API usage
int main(int argc, char* argv[]) {
std::unique_ptr<ObjectReference> ref1 = makeObject();
{
std::unique_ptr<ObjectReference> ref2 = ref1->object().makeReference();
}
ref1->object().printHello();
return 0;
}
Ссылка на предыдущее обсуждение
Этот код иллюстрирует то чего хочется достичь. Судя по тому что написано в стандарте, объект умирает после того как заканчивается вызов деструктора, если у него не тривиальный деструктор, или если записать что-либо в его память в противном случае (6.6.3 (1.3), (1.4)). В память объекта я не пишу, так что объект остаётся жить и после вызова delete. Условие что в delete expression можно сунуть только результат new expression тоже выполнено.
Хотелось обойтись без ObjectReferencePrivate в публичном API, но стандарт говорит что если статический и динамический типы объекта в delete expression различаются и нет виртуального деструктора, то UB (8.5.2.5 (3)).
Из минусов — operator new и delete в публичном API.