2010-12-23 15 views
5

En C (usando gcc) I puede declarar una struct longitud variable como a continuación:alloc una estructura con matriz de longitud cero usando nuevo

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

entonces puedo Alloc diferente del marco:

ProtocolFrame *frA; 
ProtocolFrame *frB; 

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

En FRA este ejemplo tiene un campo de carga útil tan grande como 50 bytes, y FRB no tiene carga útil

¿Puedo hacer lo mismo en C++ usando el nuevo operador?

Respuesta

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

Para decidir cuál es el tamaño en tiempo de ejecución se puede utilizar el operador colocación de nuevo en cooperación con std::malloc:

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

Usted puede leer también en la this article codeproject.com que es contener la muestra completa .

+2

Eso no es realmente lo mismo, porque al usar malloc, tiene una extensión de tamaño dinámico, mientras que esta tiene un tamaño estático. – Puppy

+0

No necesita necesariamente 'std :: malloc'. Podría escribir 'new char [sizeof (ProtocolFrame) + 50];' pero no sé si eso garantiza la alineación. –

+2

@Chris: ':: operator new (sizeof (ProtocolFrame) +50)' garantiza la alineación. –

0

Incluso en C-style, creo que esto es barbarious code.

y sizeof() su estructura seguirá devolviendo el mismo tamaño.

¿por qué no trata payload como una matriz dinámica normal?

EDIT: yo como respuesta

Edit2 Kirill V. Lyadvinsky 's: @ Chris oh veo que hay 2 llamadas a malloc ... sí lo es menos eficiente

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

Puede ser una barbaridad, pero la eficacia puede valer la pena, y las funciones API que escribe para abstraer estos detalles (usted los escribe, ¿verdad?) No serán muy diferentes de usar que el enfoque de matriz dinámica de dos asignaciones . –

+0

Mi punto es que el usuario llama 'createProtocolFrame' y no le importa qué tipo de internos bárbaros están sucediendo debajo. Si la eficiencia no es un problema, entonces el OP no se molestaría con un miembro de matriz flexible y usaría su enfoque. Sin embargo, a veces la eficiencia es un problema. –

+0

Quizás valga la pena. Si protocolFrame se crea para cada mensaje enviado y recibido, se puede crear con la frecuencia suficiente para una asignación más rápida, tiene razón. Pero no es seguro hasta que haya visto su código ;-) –

1

Por lo general, usarías 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(); 
    } 
}; 

Este estilo de código es bastante terrible, sin embargo.

+1

'vector' asigna memoria del montón, obviamente OP quiere un marco continuo. –

+0

@Krill: se puede hacer. Solo necesito editar mi respuesta un poco. – Puppy

2

Uso colocación de nueva

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

// STUFF 

frA->~ProtocolFrame(); 
delete [] buf; 

cuando se elimina FRA se llamará ProtocolFrame destructor y asignación buf libre

EDIT: He leído que no se debe llamar a borrar, pero el destructor directamente. Creo que podría ser un comportamiento específico compliler. No he usado la ubicación mucho más, pero cuando lo hice, llamé a delete y funcionó bien con MSVC++. Entonces, la forma correcta estándar parece ser frA-> ~ ProtocolFrame(); y luego eliminar buf; ¡Esto se ve horrible! Sugiero que le interese leer sobre esto.

+0

No debería estar utilizando la ubicación nueva fuera de los límites de una clase. De lo contrario, no podrá administrar la memoria correctamente cuando ocurran excepciones. –

1

En C++ que tienen clases. ¿No debería el constructor de ProtocolFrame obtener un parámetro de cuánto desea payload?

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; 
     } 
} 
+0

Necesita agregar el constructor de copia/operador de asignación o deshabilitarlos haciendo la configuración privada o alternativa en lugar de administrar la matriz, puede usar un vector. –

+0

Una 'struct' ya tiene métodos como privados. – Jaywalker

+1

una 'clase' por defecto es privada, una' struct' tiene como valor público la compatibilidad con el código C existente (que no tiene ningún concepto de privado o clases). –

1

No sé si esto sigue siendo de interés, pero se puede sobrecargar operator new para hacer esto. Esto funciona con G ++ 4.0.1 (no sé cómo "agradable" que se, sienta libre de editar y mejorarlo):

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

El uso no es demasiado horrible, ya sea:

#include <iostream> 

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

Aunque redimensionar-in- lugar puede no ser posible a través de new.

Cuestiones relacionadas