C ++: создание шаблонного общего объекта <T>вместо объекта shared_ptr <T> - PullRequest
2 голосов
/ 10 декабря 2010

В соответствии с моим предыдущим вопросом , я бы хотел, чтобы boost::shared_ptr<A> был фактически подклассом A (или, возможно, A*), чтобы его можно было использовать в методах, которые воспринимали A* как их аргумент.

Рассмотрим следующий класс:

class A
{
public:
    A(int x) {mX = x;}
    virtual void setX(int x) {mX = x;}
    virtual int getX() const {return mX;}
private:
    int mX;
};

В предыдущем вопросе я предложил создать объект SharedA, чтобы позаботиться об этом, и, предположительно, это так.

class SharedA : public A
{
public:
    SharedA(A* a) : mImpl(a){}
    virtual void setX(int x) {mImpl->setX(x);}
    virtual int getX() const {return mImpl->getX();}
private:
    boost::shared_ptr<A> mImpl;
};

Было бы подумать Grrrrrrreat, если бы я мог создать шаблонный класс, чтобы позаботиться обо всем этом для меня.

template <class T>
class Shared : public T
{
public:
    SharedT(T* t) : mImpl(t)
    {
    //What kind of crazy voodoo goes here?
    }

private:
    boost::shared_ptr<T> mImpl;
};

Если бы у меня было это (вместе с правильными конструкторами в Shared<T>), тогда я мог бы сделать следующее:

A* indestructo_A = new Shared<A>(new A(100));
A* indestructo_A_clone = new Shared<A>(indestructo_A);
delete indestructo_A
cout << "Indestructo-A back with a vengence!" << indestructo_A_clone.getX();

Вопросы:

  1. Это полезно? Или это полезность только в тех случаях, когда вы имеете дело с особенно плохим кодом. Например:

    void aFunctionYouHaveToUse (A * a) { / некоторый полезный алгоритм, а затем /
    удалить; }

  2. Возможно ли построить такой шаблонный класс? (Я полагаю, вам нужно отражение, верно?) Если вы можете построить его, как?

Ответы [ 3 ]

8 голосов
/ 10 декабря 2010

Существует действительно, чертовски веская причина, почему shared_ptr не разрешает явное приведение к A * (есть лучшие способы сделать это, чем наследовать, что в любом случае было бы невозможно).Задача shared_ptr и других интеллектуальных указателей состоит в том, чтобы предоставить небольшой инкапсулированный объект, единственной целью которого является владение указателем и решение, когда и как его удалить.

Если этот объект позволял одному и тому же указателю просто-напросто обойти его, не задумываясь, тогда он просто не мог бы служить своей цели.Каждый раз, когда вы копаете умный указатель, чтобы получить необработанный указатель внутри, вы нарушаете его семантику владения и должны затем быть очень осторожными, чтобы не делать глупостей.Умные указатели заставляют вас думать об этом, заставляя вас делать вызов, чтобы получить указатель внутри, а не просто молча делать это всякий раз, когда вы случайно передаете его неправильному виду функции.Думайте об этом, как умный указатель, говорящий: «Эй, ты знаешь, что то, что ты делаешь, может быть опасно, верно? Хорошо, тогда иди».

ИМХО, вселенная была бы лучше, если бы ее делилиуказатели не разрешали доступ к своим указателям.К сожалению, это не эта вселенная и не может быть этой вселенной, потому что иногда вам все равно нужно передать эту вещь в функцию, которая не использует умные указатели.Итак, поскольку мы не живем в этой лучшей вселенной, НАШИ умные указатели разрешают доступ, они просто не шлюхи об этом.

1 голос
/ 10 декабря 2010

Возможно, есть способ, но он довольно грязный.Вы можете попытаться поместить другой класс в вашу иерархию: ABase.A может наследоваться от ABase, а ABase будет содержать реализации по умолчанию всех открытых методов.Примерно так:

#include <iostream>
#include <boost/shared_ptr.hpp>

class ABase
{
    public:
        ABase() {}
        void init( ABase *a ) { mImpl.reset( a ); }

        virtual void setX( int x ) { mImpl->setX( x ); }
        virtual int getX() const { return mImpl->getX(); }

        boost::shared_ptr< ABase > mImpl;
};

class A : public ABase
{
public:
    typedef ABase BaseClass;

    A(int x) {mX = x;}
    virtual void setX(int x) {mX = x;}
    virtual int getX() const {return mX;}
private:
    int mX;
};


template <class T>
class Shared : public T::BaseClass
{
public:
    Shared(T* t) { init( t ); }
};

int main()
{
    Shared< A > sh( new A( 1 ) );

    std::cout << sh.getX() << std::endl;
    sh.setX( 5 );
    std::cout << sh.getX() << std::endl;
}

Это всего лишь концепция.Мне нужно проверить код.Ключевая идея состоит в том, чтобы передать реализацию по умолчанию, наследуя разделяемый ресурс не напрямую от типа A, а от его базового типа, который имеет все необходимые реализации.Базовому классу нужно еще немного поработать, но я думаю, что идея довольно ясна.

Это не совсем то, что вы просили (это на самом деле невозможно), но это может быть полезно в некоторых случаях, и, вероятно, ближе всего вы можетеполучить.

0 голосов
/ 10 декабря 2010

Откладывая в сторону, действительно ли это хорошая идея.Если вам нужен тип разделяемого указателя, который можно неявно преобразовать в необработанный указатель, вам не нужно наследовать от типа pointee или проходить через другие подобные сложности.Просто создайте тип общего указателя с соответствующими операторами неявного преобразования.

Это можно сделать путем наследования или композиции существующего типа общего указателя.(Или с помощью создания собственного общего указателя с нуля).

Использование наследования, например:

template<typename T>
class convertible_shared_ptr : public boost::shared_ptr<T>
{
public:
    convertible_shared_ptr() {}
    convertible_shared_ptr(T* ptr) : boost::shared_ptr<T>(ptr) {}

    operator T* () const
    {
        return get();
    }

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

Затем вы используете такую ​​вещь, как:1010 * Конечно, я на самом деле не пробовал такого в сценарии реального мира.При попытке взаимодействия с соответствующими типами weak_ptr могут возникнуть некоторые сложности.По крайней мере, может понадобиться еще немного кода, чтобы охватить все возможные варианты использования.

...