Newsgroup: it.comp.lang.c++
Titolo: Qualcuno me lo spiega (placement new, qualche byte di troppo)

 char* _newb=new char[(_capacity*2+2)*_tsize];
 T* _newt (_newb+_tsize) T[];
Nel mio codice esisteva una dicitura di questo tipo, poi modificata in modo
da togliere le quadre perchè causava dei problemi. Vorrei che qualcuno mi
spieghi la natura del problema, o meglio, l'origine, giacchè la natura l'ho
già compresa da me grazie alle watches.
Vi spiego: questo codice fa parte della mia procedura realloc() che rialloca
il vettore. Le righe sopra funzionavano perfettamente con gli interi ma
quando ho provato a usare le stringhe mi lanciava un eccezione e diceva
qualcosa a riguardo della violazione di memoria.
In ogni caso il fattaccio è il seguente, newb puntava ad una data locazione,
tsize era la dimensione di T e valeva 8, newt, che doveva essere piazzato in
newb+8 veniva invece piazzato in newb+12. Rimuovendo le quadre tutto torna
normale. Con gli interi tutto filava liscio (mistero della fede). Quei 4
byte in più da dove saltano fuori?
Si tratta forse della famosa memoria utilizzata per tenere conto di quanti
byte distruggere alla chiamata del delete[]?

qualcosa a riguardo della violazione di memoria.
Dire che qualcosa funziona perfettamente e' diverso
che dire che non e' crashato immediatamente.
Per poter dire se quello che hai scritto e' sensato
bisognerebbe pero' vedere codice compilabile;
quello che hai messo nel post non solo non funziona,
ma nemmeno compila.
Una implementazione di minima di una classe che
tenta di scimmiottare quanto fornito gratuitamente
da std::vector potrebbe essere questa...
  #include <string>
  #include <iostream>
  #include <memory>
  template<class T>
  class MyVect
  {
    private:
      T *data_;       // Puntatore al buffer
      int size_;      // Numero elementi validi
      int capacity_;  // Dimensione buffer (in num. elementi)
    public:
      explicit MyVect(int n = 0)
        : data_((T *)malloc(n*sizeof(T))),
          size_(0),
          capacity_(n)
      {
        if (!data_) throw std::bad_alloc();
        // Crea il numero di elementi richiesti
        try
        {
          while (size_ < n)
          {
            // Crea un nuovo elemento usando il
            // default constructor per T.
            new (data_ + size_) T();
 
            // Nota: scrivere size_++ nell'istruzione
            // precedente non e' corretto perche'
            // il costruttore di default di T potrebbe
            // lanciare una exception
            size_++;
          }
        }
        catch(...)
        {
          // In caso di problemi deve "disfare" le
          // cose qui, perche' il distruttore di
          // questa classe non verra' chiamato visto
          // che il costruttore non e' stato completato.
          while(size_ > 0)
            (data_ + --size_)->~T();
          free(data_);
          throw;
        }
      }
      // Costruttore di copia
      MyVect(const MyVect<T>& x)
        : data_((T *)malloc(x.size()*sizeof(T))),
          size_(0),
          capacity_(x.size())
      {
        if (!data_) throw std::bad_alloc();
        try
        {
          while(size_ < capacity_)
          {
            // Copia un elemento usando il
            // placement new standard e il
            // costruttore di copia di T
            new (data_ + size_) T(x[size_]);
            size_++;
          }
        }
        catch(...)
        {
          // Vedi nota sul costruttore standard
          while(size_ > 0)
            (data_ + --size_)->~T();
          free(data_);
          throw;
        }
      }
      // Operatore di assegnazione
      MyVect<T>& operator=(const MyVect<T>& x)
      {
        // Controlla che ci sia abbastanza spazio
        reserve(x.size_);
 
        // Elimina eventuali elementi in eccesso
        while (size_ > x.size_)
          (data_ + size_--)->~T();
        // Copia elemento per elemento
        for (int i=0; i<x.size_; i++)
        {
          if (i<size_)
          {
            // Usa l'assegnazione per i primi size_ elementi
            data_[i] = x[i];
          }
          else
          {
            // Usa placement new e costruttore di
            // copia per gli elementi successivi
            new (data_ + size_) T(x[i]);
            // Nota che anche in questo caso usare size_++
            // nello statement precedente sarebbe un errore.
            size_++;
          }
        }
 
        return *this;
      }
      ~MyVect()
      {
        // Distruzione degli elementi presenti
        while(size_)
          (data_ + --size_)->~T();
        free(data_);
      }
      void reserve(int m)
      {
        // Questa e' la chiamata che aumenta lo
        // spazio disponibile. Nota che il numero
        // di elementi contenuti non cambia.
        if (m > capacity_)
        {
          // Trova una nuova area di memoria.
          // Non e' possibile usare realloc perche'
          // quella funzione automaticamente potrebbe
          // fare una copia bit-a-bit del contenuto
          // per spostare degli oggetti di tipo classe
          // e questo non e' permesso.
          T *newdata = (T *)malloc(m*sizeof(T));
          if (!newdata) throw std::bad_alloc();
          // Copia gli elementi usando la forma standard
          // di placement new e il costruttore di copia.
          // Viene gestito il fatto che il loop potrebbe
          // interrompersi per colpa di una exception.
          int copied = 0;
          try
          {
            while (copied < size_)
            {
              new (newdata+copied) T(data_[copied]);
              copied++;
            }
          }
          catch(...)
          {
            // Si e' verificato un problema; elimina le
            // copie che erano riuscite e libera la memoria
            // prima di propagare l'exception.
            while(copied > 0)
              (newdata + --copied)->~T();
            free(newdata);
            throw;
          }
          // Copia riuscita; elimina gli originali e
          // punta al nuovo buffer
          while (size_ > 0)
            (data_ + --size_)->~T();
          size_ = copied;
          free(data_);
          data_ = newdata;
          // Segnala la nuova dimensione disponibile
          capacity_ = m;
        }
      }
      void push_back(const T& t)
      {
        if (size_ == capacity_)
        {
          // Se non c'e' spazio creane
          reserve(size_*2 + 2);
        }
        // Copia l'elemento passato usando la forma
        // standard di placement new e il costruttore
        // di copia dell'elemento
        new (data_ + size_) T(t);
        // Solo a questo punto incrementa size (scrivere
        // "size_++" nell'istruzione precedente non
        // sarebbe stato corretto perche' la copia avrebbe
        // potuto lanciare una exception.
        size_++;
      }
      void pop_back()
      {
        // Elimina l'ultimo elemento. Nota che la
        // dimensione disponibile non cambia.
        (data_ + --size_)->~T();
      }
      int capacity() const { return capacity_; }
      int size() const { return size_; }
      const T& operator[](int index) const { return data_[index]; }
      T& operator[](int index) { return data_[index]; }
  };
  int main()
  {
    MyVect<std::string> v(3);
    std::cout << "v.capacity() = " << v.capacity() << "\n";
    for (int i=0; i<2; i++)
      v.push_back("Andrea");
    std::cout << "v.capacity() = " << v.capacity() << "\n";
    for (int i=0; i<v.size(); i++)
      std::cout << v[i] << "\n";
    MyVect<std::string> v2 = v;
    std::cout << "v2.capacity() = " << v2.capacity() << "\n";
    v2.pop_back();
    v = v2;
    std::cout << "v.capacity() = " << v.capacity() << "\n";
    std::cout << "v.size() = " << v.size() << "\n";
    return 0;
  }
Tieni conto che questa roba l'ho scritta al volo e quindi potrebbe
contenere cavolate. Sappi anche che std::vector fornisce *molto*
piu' di questo (e dovrebbero anche esserci garanzie migliori sul
fatto che funzioni tutto a dovere ;) ) come anche che esistono
delle tecniche migliori per evitare di gestire esplicitamente
tutte queste possibili exception (esistono forme standard basate
su metodi "swap" che semplificano quei problemi e anche una
suddivisione in due classi - un gestore del solo buffer un
gestore vector - che portano a codice piu' lineare).
HTH
Andrea
PS: Giusto per curiosita' ... perche' vuoi riscriverti
    std::vector ?

contenere cavolate.
Come volevasi dimostrare.
Eseguendo il programma compilato con BCC55 tutto ha funzionato
(un minimo di verifica lo faccio sempre prima di inviare), ma
provando con g++ ho avuto subito un bel sigsegv.
L'errore e' nell'ultima istruzione che ho riportato nel
quoting che dovrebbe invece essere
         (data_ + --size_)->~T();
Questo mostra che in C++ il fatto di riuscire a compilare e
ottenere in output quello che ci si aspetta non significa
per nulla che il programma sia corretto; come anche mostra
che non si dovrebbe mai scrivere codice di questo tipo alla
domenica mattina ;)
Sorry per l'inconveniente.
Andrea

     std::vector ?
Esame di università. Da noi le librerie standard non solo non le spiegano,
ma nemmeno le fanno usare.

 la natura l'ho già compresa da me grazie alle watches.
Il codice che riporti non è ben formato, nessun compilatore potrebbe
accettarlo. Non c'è nemmeno nessun placement new; il che mi lascia supporre
che tu ti sia perso qualche pezzo per strada.
Forse volevi scrivere:
 T* _newt = new (_newb+_tsize) T[];
Ma anche questa non è un'espressione ben formata.
Max

 wrote in message
ba7qj0$2be$1...@newsreader.mailgate.org...

Dai un'occhio ai reply. In effetti mi ero sbagliato a postare. Ma poi mi
sono corretto.