2010-12-10 1 views
2

За мой предыдущий question, я хочу, чтобы boost::shared_ptr<A> был на самом деле подкласс A (или, возможно, A*), так что она может быть использована в методах, которые имели A*, как их аргумент.C++: Создание шаблонного Shared <T> объекта, а не shared_ptr <T> объект

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

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; 
}; 

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

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. Является ли это полезным ? Или это его утилита только в угловых случаях, когда вы имеете дело с особенно плохим кодом. Например:

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

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

ответ

8

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

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

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

+0

IMHO, я должен сказать во многом, что согласен с этим, но во многом не согласен. Есть много раз, когда у одного есть, скажем, контейнер с умными указателями, и он хочет иметь необработанный указатель, висящий вокруг чего-то внутри контейнера. Конечно, контейнер по-прежнему является логическим владельцем объекта, и другой код должен быть осведомлен об этом, но он позволяет вам обеспечить безопасность исключений, сделанных интеллектуальным указателем, но также не платить за управление подсчетами ссылок, а также не допускать, чтобы другой код украсть собственность и предотвратить использование shared_ptr. –

+0

Я смущен. С чем именно вы не согласны? –

+0

«ИМХО, вселенная была бы лучше, если бы общие указатели не разрешали доступ к их указателям». –

1

Возможно, есть способ, но он добрый грязный. Вы можете попробовать поставить другой класс в свою иерархию: 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, а из его базового типа, который имеет все необходимые реализации. Базовый класс нуждается в некоторой дополнительной работе, но я думаю, что идея довольно ясна.

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

+1

Черепахи полностью опущены ... –

+0

Да, но создание другого класса в иерархии - это то, чего я пытаюсь избежать. Было бы неплохо создать класс 'A', а затем использовать A * = новый Shared (новый A())' без необходимости выполнять какую-либо дополнительную работу. – JnBrymn

+0

Да, но это невозможно. Если вы строите базовый класс для всей новой иерархии классов, это может быть полезно в долгосрочной перспективе, потому что вам нужно будет делать это только один раз для каждой иерархии, но решение является жестким и только концепцией (как я уже подчеркнул). Еще один способ взглянуть на это - проблема в том, что вы в основном пытаетесь реализовать композитный шаблон дизайна без базового интерфейса. К сожалению, он не поддерживается на языке, который уже не имеет отражения, который будет ограниченным использованием в вашем случае. – LavaScornedOven

0

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

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

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

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(); 
    } 
}; 

Вы затем использовать такую ​​вещь, как так:

#include <iostream> 
#include <boost/shared_ptr.hpp> 
using namespace std; 

class A // simple class for example purposes 
{ 
public: 
    A() : member(42) {} 

    int member; 
}; 

void foo(A* a) 
{ 
    cout << "a->member=" << a->member << endl; 
} 

int main() 
{ 
    convertible_shared_ptr<A> ptr(new A); 

    foo(ptr); 

    cout << "ptr->member=" << ptr->member << endl; 

    return 0; 
} 

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