4

クラスにポインターを渡す必要があるため、制御できないコードがあります。このコードは、完了するとポインターを自動的に free() しますが、後でクラスが必要になります。コードがクラスにアクセスするのを実際に妨げずに、クラスの割り当てが解除されないようにする「ラッパー」クラスを作成できることを望んでいましたが、仮想呼び出しは機能しません。

template <class T>
class PointerWrapper:public T
{
public:
    T* p;
    PointerWrapper(T *ptr)
    {
        p=ptr;
    }

    ~PointerWrapper(void)
    {
    }
    T* operator->() const
    {
        return p;
    }
    T& operator*() const
    {
        return *p;
    }
};

void codeIDontControl(Example *ex)
{
     ex->virtualfunction();
     delete ex;
}

void myCode()
{
    Example *ex=new Example();

    codeIDontControl(ex);
    do something with ex //doesn't work because ex has been freed

    codeIDontControl(new PointerWrapper<Example>(ex));
    do something with ex //ex hasn't been freed, but the changes made to it via 
    //    Example::virtualfunction() in codeIDontControl() aren't there anymore
}

基本的に、ex->virtualfunction() は、PointerWrapper->p 内の仮想関数ではなく、PointerWrapper 自体内の仮想関数を呼び出します。-> 演算子を無視しているようですか?

これを行う別の方法があれば、PointerWrapper 風のクラスを使用する必要はありませんが、それが私が考えることができるすべてでした...

Example も変更できませんが、サブクラス化できます

4

3 に答える 3

4

仮想呼び出しを格納されたポインターにリダイレクトする Forwarder クラスを提供する必要があります。フォワーダー クラスを解放しても、pointee は解放されません。このアプローチでは、コピーを行う必要はありません(コストがかかるか、実装されていないか、または意味をなさない可能性さえあります)。

struct Forwarder : Example
{
    Example *impl;
    Forwarder(Example *i) : impl(i) {}
    void virtualfunction()
    {
        impl->virtualfunction();
    }
};

完全なコード:

ライブデモ:

#include <iostream>
#include <ostream>

using namespace std;

struct Example
{
    virtual void virtualfunction()=0;
    virtual ~Example() {}
};

struct Implmenetation : Example
{
    bool alive;
    Implmenetation() : alive(true) {}
    void virtualfunction()
    {
        cout << "Implmenetation::virtualfunction alive=" << alive << endl;
    }
    ~Implmenetation()
    {
        alive=false;
        cout << "Implmenetation::~Implmenetation" << endl;
    }
};

struct Forwarder : Example
{
    Example *impl;
    Forwarder(Example *i) : impl(i) {}
    void virtualfunction()
    {
        impl->virtualfunction();
    }
};

void codeIDontControl(Example *ex)
{
     ex->virtualfunction();
     delete ex;
}

void myCode()
{
    Implmenetation impl;

    codeIDontControl(new Forwarder(&impl));
    //do something with ex //doesn't work because ex has been freed
    impl.virtualfunction();
}
int main()
{
    myCode();
}

出力は次のとおりです。

Implmenetation::virtualfunction alive=1
Implmenetation::virtualfunction alive=1
Implmenetation::~Implmenetation
于 2012-11-11T02:27:23.687 に答える
3

設計が悪いですよね、本当に。アロケータのみがメモリを解放できるようにする必要があります。このような関数は、ダングリング ポインターで終了するため、危険です。

これは私の頭のてっぺんから外れています。おそらく、このようなことを試してみてください。安全なアイデアではありませんが、誰かがそれを実装した場合、何が起こるか知りたいです.

class Foo
{
  Foo(Foo* copy) : m_copy(copy) {}
  ~Foo() { if(m_copy) { *m_copy = *this; } } // Use copy constructor to create copy on destuction.

  Foo* m_copy;
}


Foo copy(NULL);
Foo* original = new Foo(&copy);
MethodThatDeletes(original);

// Original should be destroyed, and made a copy in the process.
original = NULL;

// Copy should be a copy of the original at it's last know state.
copy;
于 2012-11-11T02:28:13.183 に答える
0

CodeIDontControl に Example* を提供しています。PointerWrapper のオーバーロードされた operator-> は、Example* 型や PointerWrapper* 型 (つまり、その型へのポインターではなく、その型の値または参照) ではなく、PointerWrapper 型に対するものです。

呼び出す必要のある関数はユーザーによって制御されないため、有効期間を制御したいインスタンスのラッパーとして期待されるタイプの完全なラッパーを提供する必要があります。

于 2012-11-11T02:11:47.727 に答える