5 votes

allouer une structure avec un tableau de longueur nulle en utilisant new

En C (en utilisant gcc) je peux déclarer un struct de longueur variable comme ci-dessous :

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

alors je peux attribuer un cadre différent :

ProtocolFrame *frA;
ProtocolFrame *frB;

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

Dans cet exemple, frA a un champ de charge utile de 50 octets, et frB n'a pas de charge utile.

Puis-je faire la même chose en C++ en utilisant l'opérateur new ?

9voto

Kirill V. Lyadvinsky Points 47627
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>;

Pour décider de la taille au moment de l'exécution, vous pouvez utiliser l'opérateur placement-nouveau en coopération avec l'option std::malloc :

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

Vous pouvez également lire cet article sur le site codeproject.com qui contient l'échantillon complet.

2voto

T33C Points 1636

Utilisation du placement nouveau

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

// STUFF

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

quand vous supprimez frA, il appellera le destructeur de ProtocolFrame et libérera l'allocation de buf

EDIT : J'ai lu que vous ne devriez pas appeler delete mais le destructeur directement. Je pense que c'est peut-être un comportement spécifique au compilateur. Je n'ai pas beaucoup utilisé placement new mais quand je l'ai fait, j'ai appelé delete et cela a bien fonctionné avec MSVC++. Donc la méthode standard correcte semble être frA->~ProtocolFrame() ; et ensuite delete buf ; Cela semble horrible ! Je vous suggère de vous documenter sur le sujet.

1voto

Puppy Points 90818

Typiquement, vous utiliserez 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();
    }
};

Ce style de code est assez terrible, cependant.

1voto

Jaywalker Points 1455

En C++, vous avez des classes. Le constructeur de ProtocolFrame obtenir un paramètre quant à la quantité payload que vous voulez ?

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

1voto

Chris Lutz Points 34157

Je ne sais pas si c'est toujours d'intérêt, mais vous pouvez surcharger operator new pour le faire. Cela fonctionne avec G++ 4.0.1 (je ne sais pas à quel point il est "beau", n'hésitez pas à le modifier et à l'améliorer) :

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

L'utilisation n'est pas non plus trop horrible :

#include <iostream>

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

Bien que le redimensionnement sur place ne soit pas possible par l'intermédiaire de la fonction new .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X