C++ 常见的C模式,但表示为C++;?

C++ 常见的C模式,但表示为C++;?,c++,c,design-patterns,protocol-buffers,C++,C,Design Patterns,Protocol Buffers,C编程中的一种常见模式涉及可变长度结构,例如: typedef struct { int length; char data[1]; } MyBuffer; 其中数据不是[1]的数组。相反,其可变长度由length定义 结构分配如下: MyBuffer* pBuff = malloc(sizeof(MyBuffer) + 100); 我想使用相同的模式,但是在C++代码中,所以使用新< /COD> >代码>删除,而不是 Malc >代码>免费< /代码> 这个模式可以在C

C编程中的一种常见模式涉及可变长度结构,例如:

typedef struct {
    int length;
    char data[1];
} MyBuffer;
其中数据不是[1]的数组。相反,其可变长度由
length
定义

结构分配如下:

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

我想使用相同的模式,但是在C++代码中,所以使用<代码>新< /COD> >代码>删除<代码>,而不是<代码> Malc >代码>免费< /代码>

<>这个模式可以在C++代码中使用吗?怎么做

编辑因为一些答案和评论建议我切换到
std::vector

我从第三方C库获得了结构定义
MyBuffer

在我的C++应用程序中,我需要分配缓冲区并调用C库中的函数。 在边界的“我的边”,我更喜欢保持C++,并将这个结构分配给C++。
但是我仍然需要把它传递给一个C库,它不能理解任何代码,比如“代码> STD::vector < /COD> .< /P> < P>如果你需要保持与你使用的C代码的兼容性,那么它就用C++工作,几乎没有变化(只是需要从<代码> MalCube()/代码>中返回)。

这是相当麻烦的,我承认……/P> < P>这不是C++中的惯用用法,也不需要。该语言提供了

std::vector
,它为您包装了大小和缓冲区,或者如果您在运行时不需要动态调整大小,C++11提供了
std::array


编辑:这里要注意的关键是不要在堆上单独分配
向量
!如果将向量按值放在堆栈上或另一个对象中,并在构造时适当调整其大小,则使用的分配数量将与C版本(一个)完全相同。但是,你将使用习惯语言特性,防止自己产生各种各样的内存错误和/或泄漏。

C++中可以使用模板,而C元素缺少。模板参数确定数组的大小

template <unsigned N>
struct MyBufferTemplate {
    int length;
    char data[N];
};

我认为这会起作用,有几个语法错误

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];
};
classmybuffer{
//重要信息,此类不能有任何虚拟方法。
公众:
void*运算符新(大小数据长度){
MyBuffer*buffer=static_cast(新字符[sizeof(MyBuffer)+数据长度];
缓冲区->长度=数据长度;
返回缓冲区;
}
私人:
整数长度;
字符数据[1];
};
编辑:


这种技术的一个主要缺点是调试构建很常见地覆盖全局运算符new,提供对缓冲区溢出和内存泄漏的运行时检查。我不确定这会与全球运营商new的非标准实现有多好的互动。

就我个人而言,我会选择
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));
}
#包括
结构MyBuffer{
整数长度;
字符数据[1];
};
MyBuffer*使_成为_缓冲区(整数大小)
{
//分配足够大的缓冲区以满足我们的需要
char*raw_memory=new char[sizeof(MyBuffer)+size];
//调用placement new将MyBuffer放入原始内存
MyBuffer*buffer=新的(原始内存)MyBuffer;
缓冲->长度=大小;
返回缓冲区;
}
无效销毁缓冲区(MyBuffer*buffer)
{
//在本例中,MyBuffer有一个普通的(默认)析构函数,所以它不是
//确实需要,但在其他情况下,您可能需要致电
//析构函数
//
//注意:有新放置,但没有删除放置
//这是正确销毁对象的唯一方法
buffer->~MyBuffer();
//我们已经摧毁了这个物体,现在我们需要释放
//内存,幸运的是我们知道我们从new[]获得了它,所以我们可以
//删除它
删除[]静态广播(静态广播(缓冲区));
}

如果您必须使用C结构,但是希望在C++中使用更好的方法,可以使用模板和继承的组合:

#include <iostream>
#include <memory>
#include <stdlib.h>

// Here's your C struct.
// Old C-style usage would be:  MyBuffer* pBuff = malloc(sizeof(MyBuffer) + 100);
// Which effectively gives you a 101-byte array for 'data'.
// (1 for the original array, +100).
typedef struct {
    int length;
    char data[1];
} MyBuffer;

// This defines a generic template that inherits from whatever you want, and
// adds some padded space to the end of it.  The 'extra_bytes' is equivalent
// to the '+100' you used to do in the c-style malloc trick (i.e. this still
// allocates a 101-byte array for 'data').
template<typename T, size_t extra_bytes>
struct ExtraBytes : public T {
  char padding[extra_bytes];
};

// If you just want to wrap your one struct, you can use this.  The constructor
// even sets the length for you.
template<size_t array_size>
struct MyBufferWrapper : public MyBuffer {
  char padding[array_size - 1];  // 1 is already allocated to 'data'
  MyBufferWrapper() {
    length = array_size;
  }
};

int main(int, char**) {
  MyBuffer normal;
  std::cout << "Sizeof normal MyBuffer = " << sizeof(normal) << "\tlength = "
         << normal.length << "\n";  // length is uninitialized

  MyBuffer* pBuff = static_cast<MyBuffer*>(malloc(sizeof(MyBuffer) + 100));
  std::cout << "Sizeof malloc'd MyBuffer = " << sizeof(*pBuff) << "\tlength = "
         << pBuff->length << "\n";  // length is uninitialized

  ExtraBytes<MyBuffer, 100> extra_bytes;
  std::cout << "Sizeof templated ExtraBytes = " << sizeof(extra_bytes)
         << "\tlength = " << extra_bytes.length << "\n";  // length is uninitialized

  MyBufferWrapper<100> wrapper;
  std::cout << "Sizeof Wrapped MyBuffer = " << sizeof(wrapper)
         << "\tlength = " << wrapper.length << "\n";  // length is set to 100

  // If you reall  auto heap = std::make_shared<MyBufferWrapper<100>>();
  auto heap = std::make_shared<MyBufferWrapper<100>>();
  std::cout << "Sizeof heap-allocated Wrapper = " << sizeof(*heap)
         << "\tlength = " << heap->length << "\n";  // length is 100

  return 0;
}
#包括
#包括
#包括
//这是你的C结构。
//旧的C风格用法是:MyBuffer*pBuff=malloc(sizeof(MyBuffer)+100);
//这实际上为“数据”提供了一个101字节的数组。
//(原始阵列为1,+100)。
类型定义结构{
整数长度;
字符数据[1];
}我的缓冲区;
//这定义了一个通用模板,该模板继承您想要的任何内容,并且
//在其末尾添加一些填充空间。“额外字节”是等效的
//在c风格的malloc技巧中使用的“+100”(即,这仍然是
//为“数据”分配一个101字节的数组。
模板
结构外部字节:公共T{
字符填充[额外字节];
};
//如果你只想包装你的一个结构,你可以使用这个。构造器
//甚至为您设置长度。
模板
结构MyBufferWrapper:公共MyBuffer{
字符填充[array_size-1];//1已分配给“data”
MyBufferWrapper(){
长度=数组大小;
}
};
int main(int,char**){
MyBuffer正常;
STD::Cuth> P> C++ C++“ISH”的方法是将缓冲区描述为“简单的可复制”(C++ 11的LINGO,是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
{
整数长度;
字符数据[1];
私人:
MyBuffer(){}
MyBuffer&operator=(MyBuffer&other){return other;}
};
类MyBufferPointer
{
我的缓冲区*bufptr_;
静态std::size\u t getsize(std::size\u t array\u size)
{
返回sizeof(MyBuffer)+数组_size*sizeof(char);
}
静态MyBuffer*getbuf(标准::大小数组长度)
{
std::size\u t sz=getsize(数组长度);
返回静态_cast(malloc(sz));
}
公众:
MyBufferPointer(){bufptr_U8;=NULL;}
MyBufferPointer(标准::大小数组长度)
{
bufptr_uu=getbuf(数组长度);
#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));
}
#include <iostream>
#include <memory>
#include <stdlib.h>

// Here's your C struct.
// Old C-style usage would be:  MyBuffer* pBuff = malloc(sizeof(MyBuffer) + 100);
// Which effectively gives you a 101-byte array for 'data'.
// (1 for the original array, +100).
typedef struct {
    int length;
    char data[1];
} MyBuffer;

// This defines a generic template that inherits from whatever you want, and
// adds some padded space to the end of it.  The 'extra_bytes' is equivalent
// to the '+100' you used to do in the c-style malloc trick (i.e. this still
// allocates a 101-byte array for 'data').
template<typename T, size_t extra_bytes>
struct ExtraBytes : public T {
  char padding[extra_bytes];
};

// If you just want to wrap your one struct, you can use this.  The constructor
// even sets the length for you.
template<size_t array_size>
struct MyBufferWrapper : public MyBuffer {
  char padding[array_size - 1];  // 1 is already allocated to 'data'
  MyBufferWrapper() {
    length = array_size;
  }
};

int main(int, char**) {
  MyBuffer normal;
  std::cout << "Sizeof normal MyBuffer = " << sizeof(normal) << "\tlength = "
         << normal.length << "\n";  // length is uninitialized

  MyBuffer* pBuff = static_cast<MyBuffer*>(malloc(sizeof(MyBuffer) + 100));
  std::cout << "Sizeof malloc'd MyBuffer = " << sizeof(*pBuff) << "\tlength = "
         << pBuff->length << "\n";  // length is uninitialized

  ExtraBytes<MyBuffer, 100> extra_bytes;
  std::cout << "Sizeof templated ExtraBytes = " << sizeof(extra_bytes)
         << "\tlength = " << extra_bytes.length << "\n";  // length is uninitialized

  MyBufferWrapper<100> wrapper;
  std::cout << "Sizeof Wrapped MyBuffer = " << sizeof(wrapper)
         << "\tlength = " << wrapper.length << "\n";  // length is set to 100

  // If you reall  auto heap = std::make_shared<MyBufferWrapper<100>>();
  auto heap = std::make_shared<MyBufferWrapper<100>>();
  std::cout << "Sizeof heap-allocated Wrapper = " << sizeof(*heap)
         << "\tlength = " << heap->length << "\n";  // length is 100

  return 0;
}
#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;
}