Come impedire la modifica dei dati dell'array?


9

Supponiamo che io abbia una classe simile a questa (questo è solo un esempio):

class A {
    double *ptr;
public:
    A() : ptr( new double[100] ) {}
    A( const A &other ) {
        other.ptr[7] = 15;
    }
    void doNotChangeMyData() const {
        ptr[43] = 14;
    }
    void changeMyData() {
        ptr[43] = 14;
    }
    ~A() { delete[] ptr; }
};

L' constsia il costruttore di copia e la doNotChangeMyDatafunzione di fare in modo che ptrnon può essere modificato; tuttavia, ciò mi consente ancora di modificare il contenuto dell'array indicato da ptr.

Esiste un modo per impedire che il contenuto ptrdell'array venga modificato constsolo nelle istanze, a meno di "stare attenti" (o passare dal puntatore non elaborato)?

So che potrei fare qualcosa del genere

void doNotChangeMyData() const {
    const double *const ptr = this->ptr;
    ptr[43] = 14; // then this would fail to compile
}

Ma preferirei non dover ...


1
potresti usare astd::vector
idclev 463035818 il

std::vector::operator[]()può modificare i valori giusto?
marvinIsSacul

@ formerlyknownas_463035818 Domanda modificata, quindi non un'opzione;) È più una domanda teorica, ma sì, vectorfunzionerebbe.
ChrisMM,

2
@marvinIsSacul certo, ma std::vector::operator[]() constrestituisce un constriferimento
idclev 463035818

@ChrisMM quello che mi aspettavo, volevo solo menzionare l'elefante nella stanza :)
idclev 463035818

Risposte:


7

I puntatori non si propagano const. L'aggiunta constal tipo double*produce double* const, il che si traduce in un non- constvalore quando non referenziato.

Invece, puoi usare un std::vector:

class A {
    std::vector<double> data(100);
public:
    // no explicit copy ctor or dtor
};

a std::array:

class A {
    std::array<double, 100> data{};
public:
    // no explicit copy ctor or dtor
};

o un array incorporato (non consigliato):

class A {
    double data[100] {};
public:
    // no explicit copy ctor or dtor
};

Tutte e tre le opzioni si propagano const.

Se vuoi davvero usare i puntatori (fortemente sconsigliato), almeno usa a std::unique_ptrper evitare la gestione manuale della memoria. È possibile utilizzare il std::experimental::propagate_constwrapper dalle basi della libreria 2 TS:

class A {
    std::experimental::propagate_const<std::unique_ptr<double[]>> ptr;
public:
    A()
        : ptr{new double[100] {}}
    {
    }
    // manual copy ctor
    A(const A& other)
        : ptr{new double[100]}
    {
        std::copy_n(other.ptr.get(), 100, ptr.get());
    }
    // defaulted move ctor & dtor
    // assignment operator, etc.
    // ...
};

Non è ancora nello standard, ma molti compilatori lo supportano. Naturalmente, questo approccio è inferiore ai contenitori adeguati.


cercando di farlo senza cambiare il tipo di dati sottostante, più una domanda teorica che altro. Se non è possibile, lo accetterò come non possibile.
ChrisMM,

@ChrisMM Ho aggiornato la risposta con una soluzione puntatore. Ma perché :)
LF

"Perché" è difficile rispondere, più una curiosità. "Array incorporato" o std::arraynon funziona, se non conosci le dimensioni al momento della compilazione. vectoraggiunge sovraccarico; unique_ptrnon aggiunge un sovraccarico, ma se il puntatore deve essere condiviso, è necessario shared_ptraggiungere un sovraccarico. Non credo che VS attualmente supporti propagate_const(almeno il file di intestazione a cui fa riferimento cppreference non esiste con /std:c++latest) :(
ChrisMM,

1
@ChrisMM Il sovraccarico di vectorTBH è spesso sopravvalutato, soprattutto rispetto allo sforzo di gestione manuale della memoria. Inoltre, se condividi i puntatori manualmente, devi utilizzare un conteggio dei riferimenti, quindi l'overhead non è peculiare shared_ptr. Non sapevo che VS non supporta propagate_constancora (GCC e Clang lo supportano entrambi IIRC), ma non è difficile implementare il nostro secondo le specifiche.
LF

Concordo sul fatto che l'overhead è minimo, ma ci sono ragioni per utilizzare i puntatori non elaborati quando le prestazioni sono fondamentali (memoria e tempo). A volte uso a vectorquindi prendo il suo contenuto tramite .data()o &vec[0]e invece lavoro direttamente con quello. Nel caso del condiviso, ho spesso un proprietario del puntatore che crea ed elimina, ma altre classi condividono i dati.
ChrisMM,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.