4

C プログラミングの一般的なパターンには、次のような可変長構造が含まれます。

typedef struct {
    int length;
    char data[1];
} MyBuffer;

data は文字通り [1] の配列ではありません。代わりに、その可変長は によって定義されlengthます。

構造体は次のように割り当てられます。

MyBuffer* pBuff = malloc(sizeof(MyBuffer) + 100);

同じパターンを使用したいのですが、C++ コードで、/ の代わりに / を使用newdeleteますmallocfree

これと同じパターンを C++ コードで使用できますか? どのように?

編集いくつかの回答とコメントが示唆しているので、次のように切り替えstd::vectorます:

MyBufferサードパーティの C ライブラリから構造定義が提供されています。
私の C++ アプリでは、バッファを割り当て、C ライブラリの関数を呼び出す必要があります。

境界の「私の側」では、私は物事を C++ のままにし、この構造体を C++ の方法で割り当てることを好みますが、std::vector.

4

7 に答える 7

6

使用している既存の C コードとの互換性を維持する必要がある場合は、ほとんど変更せずに C++ で動作します (からの戻り値をキャストする必要があるだけですmalloc())。

#include <stdlib.h>

typedef struct {
    int length;
    char data[1];
} MyBuffer;

void f() {

    MyBuffer* pBuff = (MyBuffer *)malloc(sizeof(MyBuffer) + 100);
}

これは、g++ を使用して問題なくコンパイルされます。

割り当てられたメモリの管理が心配な場合は、malloc()それを管理するクラスを作成しMyBuffer、メンバー メソッドを介してポインタを公開できます。次に例を示します。

std::shared_ptr<MyBuffer> buf((MyBuffer *)malloc(sizeof(MyBuffer) + 100), free);

これはかなり面倒です、私は認めます...

于 2013-09-06T15:56:59.080 に答える
5

これは C++ では慣用的ではなく、必要でもありません。言語が提供std::vector<unsigned char>するサイズとバッファーをラップするか、実行時に動的なサイズ変更が必要ない場合は、C++11 が提供しますstd::array<unsigned char>

編集: ここで注意すべき点は、ヒープに単独で割り当てないことです! vectorベクトルを値渡しでスタックまたは別のオブジェクト内に配置し、構築時に適切なサイズにすると、C バージョンとまったく同じ数 (1 つ) の割り当てが使用されます。しかし、慣用的な言語機能を使用し、さまざまなメモリ エラーやメモリ リークを防ぐことができます。

于 2013-09-06T16:02:24.260 に答える
3

これでうまくいくと思いますが、いくつかの構文エラーが発生または発生します。

class MyBuffer {
 // Important, this class must not have any virtual methods.
 public:
  void* operator new(size_t data_length) {
    MyBuffer* buffer = static_cast<MyBuffer>(new char[sizeof(MyBuffer) + data_length]);
    buffer->length = data_length;
    return buffer;
  }

 private:
  int length;
  char data[1];
};

編集:

この手法の主な欠点の 1 つは、デバッグ ビルドがグローバル演算子 new をオーバーライドして、バッファー オーバーフローとメモリ リークの実行時チェックを提供することがかなり一般的であることです。これが、グローバル演算子 new の非標準実装とどの程度うまく相互作用するかはわかりません。

于 2013-09-06T16:47:25.493 に答える
1

これに取り組むC++の「っぽい」方法の1つは、バッファ自体を「簡単にコピーできる」(C++11の専門用語で、C++98および2003では「単純な古いデータ」の「POD」であった)構造体として記述することです。インスタンス化を防ぐためのプライベートコンストラクターがあるという微視的な例外。次に、その構造体のポインター オブジェクトを作成します。これは、そのアイデアを備えた完全だが自明なプログラムです。

#include <cstdlib>
#include <cstring>

struct MyBuffer
{
    int length;
    char data[1];
private:
    MyBuffer() {}
    MyBuffer& operator =(MyBuffer& other) { return other; }
};

class MyBufferPointer
{
    MyBuffer *bufptr_;

    static std::size_t getsize(std::size_t array_size)
    {
        return sizeof (MyBuffer) + array_size * sizeof (char);
    }

    static MyBuffer *getbuf(std::size_t array_length)
    {
        std::size_t sz = getsize(array_length);
        return static_cast<MyBuffer*>( malloc(sz) );
    }

public:
    MyBufferPointer() { bufptr_ = NULL; }

    MyBufferPointer(std::size_t array_length)
    {
        bufptr_ = getbuf(array_length);
        bufptr_->length = array_length;
    }

    MyBufferPointer(const MyBufferPointer &other)
    {
        const MyBuffer *op = other.bufptr_;
        if (op == NULL)
        {
            bufptr_ = NULL;
        }
        else
        {
            bufptr_ = getbuf(op->length);
            bufptr_->length = op->length;
            std::size_t sz = op->length * sizeof op->data[0];
            std::memmove( bufptr_->data, op->data, sz );
        }
    }

    MyBufferPointer& operator =(const MyBufferPointer &other)
    {
        const MyBuffer *op = other.bufptr_;
        if (op == NULL)
        {
            bufptr_ = NULL;
        }
        else
        {
            bufptr_ = getbuf(op->length);
            bufptr_->length = op->length;
            std::size_t sz = op->length * sizeof op->data[0];
            std::memmove( bufptr_->data, op->data, sz );
        }
        return *this;
    }

    ~MyBufferPointer() { if (bufptr_) free(bufptr_); }

    std::size_t size() const
    {
        return bufptr_ ? bufptr_->length : 0;
    }

    // conventience operations for access to the data array:
    char &operator [](std::size_t index) { return bufptr_->data[index]; }
    char at(size_t index) const { return bufptr_->data[index]; }
    MyBuffer* c_buffer() { return bufptr_; }
};

#include <iostream>
using namespace std;

int main()
{
    MyBufferPointer bufp;
    cout << "bufp().size() = " << bufp.size()
         << ", c_buffer=" << bufp.c_buffer() << endl;

    bufp = MyBufferPointer(100);
    cout << "bufp().size() = " << bufp.size()
         << ", c_buffer=" << bufp.c_buffer() << endl;
    return 0;
}

MyBuffer 構造体は C データ領域のレイアウトであり、プライベート コンストラクターと代入演算子の宣言のみを使用して、インスタンス化またはコピーの試行を防止します (どちらも C または C++ で適切に機能しません)。 MyBufferPointer クラスはそれを C++ としてカプセル化します。スタイル char[] 配列、[] 演算子のオーバーロード。

これは、新しいものではなく、まだ malloc() を使用しています。あなたが言及したC APIを満たすために必要なメモリイメージには可変長構造体が必要であり、newによって作成された標準C++クラスではそれを取得できません。これは、C++ ラッパーを提供して、そのクラス (静的メンバー関数 getsize() および getbuf()) 内で単一の構造体作成ポイントを提供するだけです。ポインターがスコープ外になったときのバッファーの削除を保証します。resize()、to_string()、substring()、または必要なメソッドを追加できます。

メソッドはクラス内で宣言されており、インライン化できるほど単純であるため、パフォーマンスは、最適化後に通常のポインターによってアクセスされる C 構造体と同じになるはずです。

于 2013-09-06T19:11:10.830 に答える
1

個人的には、 と を使いmallocますfree。ただし、new[]、placement new、およびdelete[]:

#include <new>

struct MyBuffer {
    int length;
    char data[1];
};

MyBuffer* make_a_buffer(int size)
{
    // allocate buffer large enough for what we want
    char* raw_memory = new char[sizeof(MyBuffer) + size];

    // call placement new to put a MyBuffer in the raw memory
    MyBuffer* buffer = new (raw_memory) MyBuffer;
    buffer->length = size;
    return buffer;
}

void destroy_a_buffer(MyBuffer* buffer)
{
    // in this case, MyBuffer has a trivial (default) destructor, so this isn't
    // really needed, but in other cases you may need to call the
    // destructor
    //
    // NOTE: there is placement new, but no placement delete
    // this is the only way to correctly destroy the object
    buffer->~MyBuffer();

    // we've destroyed the object, and now we need to release the
    // memory, luckily we know we got it from new[], so we can
    // delete[] it
    delete[] static_cast<char*>(static_cast<void*>(buffer));
}
于 2013-09-06T16:52:25.227 に答える