2010-12-23 48 views
5

在C(使用gcc)我可以声明的可变长度结构如下面零长度数组的结构体:ALLOC使用新

typedef struct ProtocolFrame 
{ 
    uint8_t  op; 
    uint32_t  address; 
    uint16_t  size; 
    uint8_t  payload[0]; 
} ProtocolFrame; 

然后我可以ALLOC不同帧:

ProtocolFrame *frA; 
ProtocolFrame *frB; 

frA = malloc(sizeof(ProtocolFrame) + 50); 
frB = malloc(sizeof(ProtocolFrame)); 

在这个例子frA有一个大于50字节的有效载荷字段,而frB没有有效载荷

我可以使用new运算符在C++中做同样的事吗?

回答

9
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>; 

要决定什么是在运行时,你可以使用合作投放新的运营商,std::malloc大小:

void *buffer = std::malloc(sizeof(ProtocolFrame)+50); 
ProtocolFrame *frA = new (buffer) ProtocolFrame; 

你可以是包含完整的样本codeproject.com也看了this article

+2

这并不完全相同,因为通过使用malloc,他有一个动态大小的扩展,而这是静态大小。 – Puppy 2010-12-23 10:26:26

+0

你不一定需要`std :: malloc`。你可以写`新字符[sizeof(ProtocolFrame)+ 50];`但我不知道这是否保证对齐。 – 2010-12-23 10:32:07

+2

@Chris:`:: operator new(sizeof(ProtocolFrame)+50)`保证对齐。 – 2010-12-23 10:42:34

0

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

和sizeof()你的结构仍然会返回相同的大小。

为什么不把​​作为一个正常的动态数组?

编辑:我喜欢基里尔五Lyadvinsky的回答

EDIT2:@克里斯哦,我看到有2调用malloc ......是的,它是效率较低

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

通常情况下,你会使用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(); 
    } 
}; 

虽然这种风格的代码是非常可怕的。

2

使用放置新

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分配

编辑

:我看过,你不应该调用删除,但析构函数直接。我认为这可能是一个compliler特定的行为。我没有使用过多的贴图,但是当我这样做时,我调用了删除,并且它在MSVC++中运行良好。所以标准的正确方法似乎是frA->〜ProtocolFrame();然后删除buf;这看起来很可怕!我建议你可能想阅读它。

1

在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; 
     } 
} 
1

不知道这是否是感兴趣的是,但你可以重载operator new做到这一点。这适用于G ++ 4.0。1(我不知道怎么“好”是,随意编辑和改进):

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

虽然调整-IN-地方可能不可能通过new