Необходимо ли использовать GC :: KeepAlive в C ++ / CLI, когда управляемый дескриптор содержится в управляемом контейнере (IList <Foo ^>)? - PullRequest
1 голос
/ 13 апреля 2011

Я не совсем понимаю, когда мне нужно использовать KeepAlive в коде оболочки C ++ / CLI и как в нем обрабатываются времена жизни. Рассмотрите следующий код и отметьте места, где я спрашиваю, нужен ли KeepAlive.

// convert from managed to native string
inline std::string ToStdString(String^ source)
{
    if (String::IsNullOrEmpty(source))
        return std::string();

    int len = ((source->Length+1) * 2);

    /*** Do I need GC::KeepAlive(source) here? ***/

    char *ch = new char[ len ];
    bool result ;
    {
        pin_ptr<const wchar_t> wch = PtrToStringChars( source );
        result = wcstombs( ch, wch, len ) != -1;
    }
    std::string target = ch;
    delete ch;
    if(!result)
        throw gcnew Exception("error converting System::String to std::string");
    return target;
}

// convert from native to managed string
inline String^ ToSystemString(const std::string& source)
{
    return gcnew String(source.c_str());
}

// unmanaged C++ class
struct NativeDog
{
    std::string name;
    std::string bark() const {return "woof";}
    void eat(std::string& food) const {food.clear();}
};

typedef shared_ptr<NativeDog> NativeDogPtr;

// C++/CLI wrapper class
ref class ManagedDog
{
    NativeDogPtr* base_;
    NativeDog& base() {return **base_;}
    ManagedDog() {base_ = new NativeDogPtr(new NativeDog);}
    ~ManagedDog() {if (base_) delete base_;}
    !ManagedDog() {delete this;}

    property String^ name
    {
        String^ get() {return ToSystemString(base().name);}
        void set(String^ name)
        {
              base().name = ToStdString(name);
              /*** Do I need GC::KeepAlive(name) here? ***/
        }
    }

    String^ bark() {return ToSystemString(base().bark());}
    void eat(String^ food)
    {
         std::string nativeFood = ToStdString(food);
         base().eat(nativeFood);
         food = ToSystemString(nativeFood);
         /*** Do I need GC::KeepAlive(food) here? ***/
    }
};

// unmanaged C++ class
struct NativeKennel
{
    vector<NativeDogPtr> dogs;
};

// C++/CLI wrapper class
ref class ManagedKennel
{
    NativeKennel* base_;
    NativeKennel& base() {return *base_;}
    IList<ManagedDog^>^ dogs;
    void addDog(ManagedDog^ dog)
    {
        base().dogs.push_back(*dog->base_);
        dogs->Add(dog);
        /*** Do I need GC::KeepAlive(dog) here? Will the IList manage the ManagedDog lifetimes? ***/
    }
};

Ответы [ 2 ]

3 голосов
/ 13 апреля 2011

Прямо перед вызовом указателя функции управляемого делегата.

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

1 голос
/ 13 апреля 2011

Ничего из перечисленного!

Если вы обращаетесь к управляемым классам в C ++ / CLI, KeepAlive не поможет. Вам необходимо закрепить данные в памяти, чтобы они не перемещались после сборки мусора. Во всех этих примерах это делается неявно с помощью вызываемых вами функций.

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

...