C++ 使用新的

C++ 使用新的,c++,malloc,new-operator,C++,Malloc,New Operator,在C(使用gcc)中,我可以声明一个可变长度结构,如下所示: typedef struct ProtocolFrame { uint8_t op; uint32_t address; uint16_t size; uint8_t payload[0]; } ProtocolFrame; 然后我可以分配不同的帧: ProtocolFrame *frA; ProtocolFrame *frB; frA = malloc(

在C(使用gcc)中,我可以声明一个可变长度结构,如下所示:

typedef struct ProtocolFrame
{
     uint8_t      op;
     uint32_t     address;
     uint16_t     size;
     uint8_t      payload[0];
} ProtocolFrame;
然后我可以分配不同的帧:

ProtocolFrame *frA;
ProtocolFrame *frB;

frA = malloc(sizeof(ProtocolFrame) + 50);
frB = malloc(sizeof(ProtocolFrame));
在这个例子中,frA有一个50字节的有效载荷字段,而frB没有有效载荷

我能用C++的新操作符做同样的事情吗?< /P>

template<size_t s>
struct ProtocolFrame
{
     uint8_t      op;
     uint32_t     address;
     uint16_t     size;
     uint8_t      payload[s];
} ProtocolFrame;

// specialize for no payload
template<>
struct ProtocolFrame<0>
{
     uint8_t      op;
     uint32_t     address;
     uint16_t     size;
} ProtocolFrame;

ProtocolFrame<50> *frA = new ProtocolFrame<50>;
ProtocolFrame<0> *frB = new ProtocolFrame<0>;

您也可以在codeproject.com上阅读,其中包含完整的示例。

即使是C风格的,我认为这是野蛮的代码

和sizeof()您的结构仍将返回相同的大小

为什么不将有效负载看作一个普通的动态数组

编辑:我确实喜欢基里尔·莱德文斯基的答案

EDIT2:@Chris哦,我知道有两个电话打给malloc。。。是的,效率较低

inline ProtocolFrame * createProtocolFrame(int i)
{
    ProtocolFrame * pProto = malloc(sizeof(ProtocolFrame));
    pProto->payload = malloc(i * sizeof(uint8_t));
}

通常,您会使用std::vector

class ProtocolFrame {
    // Invokes undefined behaviour if stuff is not POD.
    struct stuff {
        stuff(uint8_t lop, uint32_t laddress, uint16_t lsize)
            : op(lop), address(laddress), size(lsize) {
        }
        uint8_t op;
        uint32_t address;
        uint16_t size;
    };
    std::vector<uint8_t> payload;
public:
    ProtocolFrame(int payloadsize, uint8_t op, uint32_t address, uint16_t size)
        : payload(size + sizeof(stuff)) {
        new (&payload[0]) stuff(op, address, size);
    }
    // other methods here
    uint32_t GetAddress() {
        return ((stuff*)&payload[0])->address;
    }
    uint16_t GetSize() {
        return ((stuff*)&payload[0])->size;
    }
    uint8_t GetOp() {
        return ((stuff*)&payload[0])->op;
    }
    std::vector<uint8_t>::iterator begin() {
        return payload.begin() + sizeof(stuff);
    }
    std::vector<uint8_t>::iterator end() {
        return payload.end();
    }
};
类协议框架{
//如果内容不是POD,则调用未定义的行为。
结构材料{
材料(包括8层、32层、16层)
:op(lop)、地址(LADRESS)、尺寸(lsize){
}
uint8_t op;
uint32地址;
uint16_t尺寸;
};
向量有效载荷;
公众:
协议框架(int payloadsize、uint8\u t op、uint32\u t地址、uint16\u t size)
:有效载荷(尺寸+尺寸(填充物)){
新的(&payload[0])内容(op、地址、大小);
}
//这里还有其他方法
uint32_t GetAddress(){
返回((stuff*)和有效负载[0])->地址;
}
uint16_t GetSize(){
返回((填充*)和有效负载[0])->大小;
}
uint8_t GetOp(){
返回((stuff*)和有效载荷[0])->op;
}
std::vector::迭代器begin(){
返回有效负载.begin()+sizeof(stuff);
}
std::vector::迭代器end(){
返回有效载荷.end();
}
};
但是,这种风格的代码非常糟糕。

使用新的布局

char *buf  = new char[sizeof(ProtocolFrame) + 50];   //pre-allocated buffer
ProtocolFrame *frA = new (buf) ProtocolFrame;  //placement new

// STUFF

frA->~ProtocolFrame();
delete [] buf;
当您删除frA时,它将调用ProtocolFrame析构函数并释放buf分配


编辑:我读到你不应该直接调用delete而应该调用析构函数。我认为这可能是康普利尔特有的行为。我没有太多使用placement new,但当我使用它时,我调用了delete,它在MSVC++中运行良好。因此,标准的正确方法似乎是frA->~ProtocolFrame();然后删除buf;这看起来很可怕!我建议你读一下。在C++中,你有课。
ProtocolFrame
的构造函数不应该获取一个参数来确定您想要的
有效负载的多少吗

struct ProtocolFrame {
   uint8_t      op;
   uint32_t     address;
   uint16_t     size;
   uint8_t      *payload;

   public:
       ProtocolFrame (int size) {
          payload = new uint8_t [size];
       }

       ~ProtocolFrame () {
          delete [] payload;
       }
 }

不知道这是否仍然有意义,但您可以通过重载
运算符new
来执行此操作。这适用于G++4.0.1(我不知道它有多“好”,请随意编辑和改进):

#包括
模板
课堂测试{
公众:
标准:尺寸透镜;
T-arr[1];
void*运算符新(std::size\u t s,std::size\u t a);
测试(常数T&f){填充(f);}
test();
私人:
空洞填充(const T&f){for(std::size_T i=0;ilen=a;
返回p;
}
用法也不太可怕:

#include <iostream>

int main()
{
    test<char> *c = new (10) test<char>('c');
    std::cout << c->arr[3] << std::endl;
    delete c;
    return 0;
}
#包括
int main()
{
测试*c=新的(10)项测试(“c”);

std::cout-arr[3]这其实不一样,因为通过使用malloc,他有一个动态大小的扩展,而这个扩展是静态大小的。你不一定需要
std::malloc
。你可以编写
new char[sizeof(ProtocolFrame)+50];
,但我不知道这是否能保证对齐。@Chris:
::operator new(sizeof(ProtocolFrame)+50)
保证对齐。@Steve-确实如此。我砍掉了一个(有趣的?)重载new的实现,使类ProtocolFrame的实例化成员变得更容易。@Chris Lutz,在文章中有一个重载运算符new的完整解决方案。它太大了,不能放在这里,但我建议阅读它。它可能很野蛮,但效率可能是值得的,还有你编写的API函数把这些细节抽象掉(你确实写了,对吧?)与双分配动态数组方法没有太大区别。我的观点是,用户调用
createProtocolFrame
,并不关心下面发生了什么野蛮的内部结构。如果效率不是问题,那么OP就不会麻烦灵活的数组成员,而是会使用您的方法。但是r、 有时候效率是个问题。也许值得。如果为发送和接收的每一条消息创建协议框架,它可能会被创建得足够频繁,以便更快地分配,你是对的。但直到我看到他的代码后才确定;-)@Chris,是的,我已经冷静下来了我的“OOP integrist”编写createProtocolFrame()时的观点是的,有时效率是一个问题。
vector
从堆中分配内存,显然OP需要连续帧。您需要添加复制构造函数/赋值运算符,或者通过使其私有化来禁用它们,或者使用vector来管理数组。a
class
默认为私有,a
struct
默认为public,与现有C代码兼容(没有私有或类的概念)@Chris-是的,你是对的。我不确定我是在什么样的心态下写了上述语句。
struct
的所有成员都是公共的,除非进行了修改。
class
的所有成员默认都是私有的。不应该在类的范围之外使用placement new。否则,当出现异常时,你无法正确管理内存这种情况经常发生。
#include <cstddef>

template <typename T>
class test {
  public:
    std::size_t len;
    T arr[1];

    void *operator new(std::size_t s, std::size_t a);

    test(const T& f) { fill(f); }
    test();

  private:
    void fill(const T& f) { for(std::size_t i = 0; i < len; i++) arr[i] = f; }
};

template <typename T>
void *test<T>::operator new(std::size_t s, std::size_t a)
{
    void *p = ::operator new(s + (a - 1) * sizeof(T));
    // this is bad and we shouldn't do this here blah blah blah
    // but I don't know how to pass a to the ctor so this is what I did
    ((test<T> *)p)->len = a;
    return p;
}
#include <iostream>

int main()
{
    test<char> *c = new (10) test<char>('c');
    std::cout << c->arr[3] << std::endl;
    delete c;
    return 0;
}