Redimensionnement D'un c++ std::vecteur sans initialisation des données [dupliquer]

cette question a déjà une réponse ici:

avec des vecteurs, on peut supposer que les éléments sont stockés de façon contiguë dans la mémoire, permettant la gamme [&vec[0], &vec[vec.capacité()) à utiliser comme un Tableau normal. Par exemple,

vector<char> buf;
buf.reserve(N);
int M = read(fd, &buf[0], N);

mais maintenant le vecteur ne sait pas qu'il contient M bytes de données, ajouté extérieurement par read () . Je sais que vecteur:: reize () définit la taille, mais il efface également les données, de sorte qu'il ne peut pas être utilisé pour mettre à jour la taille après l'appel read () .

est-il une façon triviale de lire des données directement dans les vecteurs et mettre à jour la taille après? Oui, je connais des solutions de rechange évidentes comme l'utilisation d'un petit tableau comme tampon de lecture temporaire, et l'utilisation de vecteur:: insert () pour ajouter qu'à la fin du vecteur:

char tmp[N];
int M = read(fd, tmp, N);
buf.insert(buf.end(), tmp, tmp + M)

Cela fonctionne (et c'est ce que je fais aujourd'hui), mais ça me dérange qu'il n'y est une copie supplémentaire opération qui ne serait pas nécessaire si je pouvais mettre les données directement dans le vecteur.

Existe-t-il donc un moyen simple de modifier la taille du vecteur lorsque des données ont été ajoutées à l'extérieur?

28
demandé sur Andy Finkenstadt 2011-10-07 19:27:33

6 réponses

vector<char> buf;
buf.reserve(N);
int M = read(fd, &buf[0], N);

ce fragment de code invoque un comportement non défini. Vous ne pouvez pas écrire au-delà de size() elements, même si vous avez réservé l'espace .

le code correct est comme:

vector<char> buf;
buf.resize(N);
int M = read(fd, &buf[0], N);
buf.resize(M);


PS. votre déclaration "avec des vecteurs, on peut supposer que les éléments sont stockés de façon contiguë dans la mémoire, permettant la gamme [&vec[0], &vec[vec.capacity()) pour être utilisé comme un tableau normal" n'est pas vrai. La plage admissible est [&vec[0], &vec[vec.size()) .
23
répondu Robᵩ 2011-10-07 15:36:58

on dirait que vous pouvez faire ce que vous voulez en C++11 (bien que je n'ai pas essayé cela moi-même). Vous devrez définir un allocateur personnalisé pour le vecteur, puis utiliser emplace_back() .

d'abord, définir

struct do_not_initialize_tag {};

puis définissez votre allocator avec cette fonction de membre:

class my_allocator {
    void construct(char* c, do_not_initialize_tag) const {
        // do nothing
    }

    // details omitted
    // ...
}

maintenant vous pouvez ajouter des éléments à votre tableau sans les initialiser:

std::vector<char, my_allocator> buf;
buf.reserve(N);
for (int i = 0; i != N; ++i)
    buf.emplace_back(do_not_initialize_tag());
int M = read(fd, buf.data(), N);
buf.resize(M);

l'efficacité de ceci dépend sur le compilateur optimiseur. Par exemple, la boucle peut augmenter la taille de la variable membre N fois.

5
répondu Derek Ledbetter 2012-10-08 18:44:15

écrit dans et après l'élément size() th est un comportement non défini.

exemple suivant copie un fichier entier dans un vecteur de manière c++ (pas besoin de connaître la taille du fichier et pas besoin de préallouer la mémoire dans le vecteur):

#include <algorithm>
#include <fstream>
#include <iterator>
#include <vector>

int main()
{
    typedef std::istream_iterator<char> istream_iterator;
    std::ifstream file("example.txt");
    std::vector<char> input;

    file >> std::noskipws;
    std::copy( istream_iterator(file), 
               istream_iterator(),
               std::back_inserter(input));
}
2
répondu BЈовић 2011-10-07 16:30:42

un autre, plus récent, question, un duplicata de celui-ci , a une réponse , qui ressemble exactement à ce qui est demandé ici. Voici sa copie (de v3) pour consultation rapide:

C'est un problème connu que l'initialisation ne peut pas être désactivé, même explicitement pour std::vector .

les gens mettent normalement en œuvre leur propre pod_vector<> qui ne fait pas toute initialisation des éléments.

une autre façon est de créer un type qui est layout-compatible avec char, dont le constructeur ne fait rien:

struct NoInitChar
{
    char value;
    NoInitChar() {
        // do nothing
        static_assert(sizeof *this == sizeof value, "invalid size");
        static_assert(__alignof *this == __alignof value, "invalid alignment");
    }
};

int main() {
    std::vector<NoInitChar> v;
    v.resize(10); // calls NoInitChar() which does not initialize

    // Look ma, no reinterpret_cast<>!
    char* beg = &v.front().value;
    char* end = beg + v.size();
}
2
répondu Ruslan 2017-05-23 12:25:19

votre fragment de programme est entré dans le domaine du comportement non défini.

quand buf.empty() est vrai, buf[0] a un comportement non défini, et donc &buf[0] est également Non défini.

ce fragment fait probablement ce que vous voulez.

vector<char> buf;
buf.resize(N); // preallocate space
int M = read(fd, &buf[0], N);
buf.resize(M); // disallow access to the remainder
1
répondu Andy Finkenstadt 2011-10-07 15:33:21

vous pourriez vouloir essayer de sous-classe la classe vectorielle et mettre en œuvre une méthode qui définit la taille en accédant directement aux variables membres: dans GNU C++ ceci est fait en suivant le fragment de code:

template <typename T, typename A = mmap_allocator<T> >
class mmappable_vector: public std::vector<T, A> {
public:
    void map_into_memory(const size_t n)
    {
        std::vector<T,A>::reserve(n);
#ifdef __GNUC__
        std::vector<T,A>::_M_impl._M_finish = std::vector<T,A>::_M_impl._M_end_of_storage;
#else
#error "Not GNU C++, please either implement me or use GCC"
#endif
    }
};

et utilisez cette classe (vous devrez ajouter quelques constructeurs) au lieu de stl::vector. Cependant, à moins que vous ne manipuliez des fichiers de plus de 1 Go, cela n'en vaut pas la peine.

Si vous le faites, vous voudrez peut-être donner le mmap_allocator d'essayer (encore beta, disponible à partir de github.com/johannesthoma/mmap_allocator).

0
répondu Johannes Thoma 2012-10-08 16:57:49