1

以下のようなものを実装したいと思います。

struct MyArray {
    void* Elements;
    int Capacity;
    int ElementsCount;
    size_t ElementSize;

    //methods
    void AddElement(void* item);
    //...
};

void* Elements任意のタイプのアイテムへのポインターである必要があります。すべての要素には特定のサイズ (ElementSize変数) が必要であり、AddElement(void*)メソッドはアイテムを既存の配列に追加する必要があります。問題は、配列でポインター演算を実行できないことです。使用するたびにキャストを使用する必要があることはわかっていますが、その方法がまったくわかりません。そして、テンプレートがより良い解決策になることは知っていますが、この場合、ポインターを使って練習したいと思います:)

事前に助けてくれてありがとう。

4

5 に答える 5

2

ポインタを移動するには、次のようにします。

int* nextInt = reinterpret_cast<int*>(Elements) + 1;

これは次を指しintます。この手法を使用して、他のタイプを移動できます。

要素のサイズが異なるため、これはあらゆる種類の問題につながる可能性があることに注意してください。

于 2012-10-22T10:54:00.783 に答える
0

なぜあなたがこれをしたいのか疑問に思う必要があります。学術的/実験的な目的でこれを行い、それを破棄する計画を立てていない限り、あなたは自分で仕事をしていて、言語とSTLの機能を使用した場合よりも問題に対して脆弱なコードになってしまうことはほぼ確実です。すでに提供しています。Cでは、おそらくこれを行う必要がありますが、C ++では、言語サポートがあるため、これを行う必要はありません。

あなたがしていることには2つの側面があります:ある定義された方法で一般的に使用できる任意のタイプの要素を持つことと、それらの要素を一緒に集めることです。そうすれば、最初の側面は多形性によって簡単に達成できます。共通インターフェースを定義する抽象基本クラスを作成します。

struct BaseElement { virtual void doSomething( ); };

次に、これから、要素が実行していることをカバーする構造体を導出できます。

struct DerivedElement1 : public BaseElement { void doSomething( ); };

struct DerivedElement2 : public BaseElement { void doSomething( ); };

タイプをまとめるには、STLベクトルを使用するだけです。私が見る限り、それはあなたが必要とするすべてのものを提供します。非常に簡単な例として、次のようにすることができます。

// Convenient shorthand.
typedef std::vector< std::shared_ptr<BaseElement> > MyElements;
MyElements m;

// Create two different but commonly derived objects.
std::shared_ptr<DerivedElement1> e1(new DerivedElement1);
std::shared_ptr<DerivedElement2> e2(new DerivedElement2);

// Push them onto the collection.
m.push_back( e1.static_pointer_cast<BaseElement>( e1 ) );
m.push_back( e2.static_pointer_cast<BaseElement>( e2 ) );

この時点で、必要なものはすべて揃っています。begin()Vectorは、end()などの標準機能を提供しますsize()。これは、必要に応じて、コレクションをトラバースし、コレクションでSTLアルゴリズムを実行するのに役立ちます。コレクションが多態的であるという事実は、doSomething()その構造体に対して定義されたものだけを実行することを知っている各要素で実行できることを意味します。

(私はC ++ 11コンパイラにアクセスしていないので、誰かがここで私を迎えてくれると確信しています。ただし、生のポインタを使用しても、C++11より前のコードで同じことが簡単に達成できます。 'オブジェクトを適切にクリアするように注意してください。)

これがあなたが直接望んでいた答えではないことは知っていますが、単に捨てる例で学ぼうとしているのでない限り、ほとんどの場合、すでにあるものを使用する方が速く、短く、安全で、信頼性が高いことを強調したいと思います。

于 2012-10-22T11:53:30.083 に答える
0

私はこのように追求します:

  1. STL のような配列固有のイテレータを作成します。
  2. イテレータのオーバーロード演算子

ストレージタイプの知識がなくても配列を反復処理する汎用アルゴリズムがある場合があるため、反復子は配列に役立ちます。

于 2012-10-22T11:00:00.023 に答える
0

はい、void* でポインター演算を行うことはできません。演算を行うには、char* にキャストする必要があります。たとえば、次のようになります。

void MyArray::AddElement( void * item )
{
    // verify that ElementsCount is not already Capacity and if so, reallocate or throw
    void * insertionPoint = static_cast<char *>(Elements) + (ElementSize * ElementsCount );
    memcpy( insertionPoint, item, ElementSize );
    ++ElementsCount;
}

static_castvoid* から char* にキャストする必要があり、void* に明示的にキャストする必要がないことに注意してください。これが、insertionPoint に割り当てることができる理由です。

于 2012-10-22T10:56:28.240 に答える
0

テンプレートとポインターがどのように相互に排他的であるかはわかりません。これが、人々が最初にそれらを使用する理由のような状況だと思います。テンプレートを使用してポインターに型を指定すると、問題がソートされます。

一方、テンプレートを完全に回避すると、型のサイズが必要になります。たとえば、CashCow が問題を処理する方法は次のとおりです。

void MyArray::AddElement( void * item )
{
    auto insertionPoint = static_cast<char *>(Elements) + (ElementSize * ElementsCount );
    memcpy( insertionPoint, item, ElementSize );
    ++ElementsCount;
}

それでも、あなたはそれで終わっていません。事前に割り当てられたバッファーを決して超えないようにする必要があります。このように変更します:

void MyArray::AddElement( void * item )
{
  if ((Capacity + 1) < ElementSize * ElementsCount)
  {
    Capacity <<= 1; // Double the size of the buffer.
    auto newBlock = new char[Capacity];
    memcpy(Elements, newBlock, Capacity >> 1); // Copy the old data
    delete Elements;
    Elements = static_cast<void*>(newBlock);
  }

  auto insertionPoint = static_cast<char *>(Elements) + (ElementSize * ElementsCount );
  memcpy( insertionPoint, item, Elementize );
  ++ElementsCount;
}

このようなもの。もちろん、これはまだ完全ではありませんが、おそらく手がかりになります。

于 2012-10-22T11:17:54.960 に答える