2014-09-24 3 views
2

Я хочу быстро реализовать то, что некоторые называют «указателем владельца», то есть интеллектуальным указателем, обеспечивающим уникальную семантику собственности, предоставляя при этом указатели «наблюдателя», которые не поддерживают объект живой, но может проверить, есть ли это.Forbid copy-construction базового класса

Самый простой способ, которым я пытаюсь это сделать, - это подкласс std::shared_ptr и отключить его конструкцию копирования, чтобы ни один другой указатель не мог фактически обмениваться объектом.

Это то, что я имею сейчас:

#include <memory> 
#include <iostream> 

template <class T> 
struct owner_ptr : public std::shared_ptr<T> { 

    // Import constructors 
    using std::shared_ptr<T>::shared_ptr; 

    // Disable copy-construction 
    owner_ptr(owner_ptr<T> const&) = delete; 

    // Failed attempt at forbidding what comes next 
    operator std::shared_ptr<T> const&() = delete; 
}; 

struct Foo { 
    Foo() { 
     std::cout << "Hello Foo\n"; 
    } 

    ~Foo() { 
     std::cout << "G'bye Foo\n"; 
    } 

    void talk() { 
     std::cout << "I'm talkin'\n"; 
    } 
}; 

owner_ptr<Foo> fooPtr(new Foo); 

int main(int, char**) { 

    // This should not compile, but it does. 
    std::shared_ptr<Foo> sptr = fooPtr; 

    // Simple tests 
    fooPtr->talk(); 
    (*fooPtr).talk(); 

    // Confirmation that two pointers are sharing the object (it prints "2"). 
    std::cout << sptr.use_count() << '\n'; 
} 

Я потянув мои волосы на это. Как запретить копирование конструкции std::shared_ptr с моего owner_ptr? Я не люблю наследовать в частном порядке, а затем импортировать все от std::shared_ptr ...

+2

Вы действительно хотите использовать композицию вместо наследования здесь. Особенно не публичная база. Это источник всех ваших проблем. – Deduplicator

+0

@Deduplicator Я бы перенаправил все сказанному члену, что совершенно так же, как и в моем последнем предложении. Я сделал бы это в крайнем случае, если ничего не поделаешь, но я не люблю этого. – Quentin

+0

Итак, вы хотите создать что-то вроде 'std :: unique_ptr'? –

ответ

1

Я не думаю, что подкласс std::shared_ptr - это путь. Если вы действительно хотели сделать это правильно, я думаю, вы должны реализовать его самостоятельно, включая все подсчет ссылок. Реализация умного указателя на самом деле не , что сложно.

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

мне было любопытно, что вы пытаетесь сделать, я не уверен, что это хорошая идея, но я должен был пойти на реализацию OwnerPointer и ObserverPointer пары, используя состав:

#include <memory> 
#include <iostream> 

struct Foo { 
    Foo() {std::cout << "Hello Foo\n"; } 
    ~Foo() { std::cout << "G'bye Foo\n"; } 
    void talk() { std::cout << "I'm talkin'\n"; } 
}; 

template <class T> 
class ObserverPointer; // Forward declaration. 

template<class T> 
class OwnerPointer; // Forward declaration. 

// RAII object that can be obtained from ObserverPointer 
// that ensures the ObserverPointer does not expire. 
// Only operation is to test validity. 
template <class T> 
class ObserverLock { 
friend ObserverPointer<T>; 
private: 
    std::shared_ptr<T> impl_; 
    ObserverLock(const std::weak_ptr<T>& in) : impl_(in.lock()) {} 
public: 
    // Movable. 
    ObserverLock(ObserverLock&&) = default; 
    ObserverLock& operator=(ObserverLock&&) = default; 

    // Not copyable. 
    ObserverLock& operator=(const ObserverLock&) = delete; 
    ObserverLock(const ObserverLock&) = delete; 

    // Test validity. 
    explicit operator bool() const noexcept { return impl_ != nullptr;} 
}; 

template <class T> 
class ObserverPointer { 
private: 
    std::weak_ptr<T> impl_; 
    T*    raw_; 
public: 
    ObserverPointer(const OwnerPointer<T>& own) noexcept : impl_(own.impl_), raw_(own.get()) {} 

    T* get() const { return raw_; } 
    T* operator->() const { return raw_; } 
    T& operator*() const { return *raw_; } 

    ObserverPointer() : impl_(), raw_(nullptr) { } 
    ObserverPointer(const ObserverPointer& in) = default; 
    ObserverPointer(ObserverPointer&& in) = default; 
    ObserverPointer& operator=(const ObserverPointer& in) = default; 
    ObserverPointer& operator=(ObserverPointer&& in) = default; 

    bool expired() { return impl_.expired(); } 
    ObserverLock<T> lock() { return ObserverLock<T>(impl_); } 
}; 

template <class T> 
struct OwnerPointer {  
friend ObserverPointer<T>;  
private: 
    std::shared_ptr<T> impl_; 
public: 

    // Constructors 
    explicit OwnerPointer(T* in) : impl_(in) {} 
    template<class Deleter> 
    OwnerPointer(std::unique_ptr<T, Deleter>&& in) : impl_(std::move(in)) { } 
    OwnerPointer(std::shared_ptr<T>&& in) noexcept : impl_(std::move(in)) { } 
    OwnerPointer(OwnerPointer<T>&&) noexcept = default; 
    OwnerPointer(OwnerPointer<T> const&) = delete; 

    // Assignment operators 
    OwnerPointer& operator=(OwnerPointer<T> const&) = delete; 
    OwnerPointer& operator=(OwnerPointer<T>&&) = default; 

    T* get() const { return impl_.get(); } 
    T* operator->() const { return impl_.get(); } 
    T& operator*() const { return *impl_; } 

    explicit operator ObserverPointer<T>() const noexcept { return ObserverPointer<T>(impl_);} 
    explicit operator bool() const noexcept { return impl_;} 
}; 

// Convenience function equivalent to make_shared 
template <class T, class... Args> 
OwnerPointer<T> make_owner(Args && ...args) { 
    return OwnerPointer<T>(new T(std::forward<Args>(args)...)); 
} 

int main() { 
    auto owner = make_owner<Foo>(); 
    ObserverPointer<Foo> observer = owner; 
    auto lock = observer.lock(); 
    if (lock) 
    observer->talk(); 
} 

Live demo.

Это, вероятно, нуждается в некоторой работе, и он не предлагает полный набор функций std :: shared_ptr & std :: weak_ptr, но тогда в большинстве случаев это не понадобится, просто создайте то, что вам нужно.

Я потянул определение «уникальное владение», предложив объект RAII ObserverLock, который может использоваться только для сохранения живого ObserverPointer. Технически он «владеет» указателем, но он очень ограничен в том, что он может сделать, и вы не можете создать более одного «OwnerPointer».

+0

Хотя он не отвечает на мой вопрос, он решает мою проблему. Я собирался свернуть рукава и реализовать его сам, но я бы не подумал о «ObserverLock», как вы его разработали. Благодаря ! – Quentin

Смежные вопросы