Як запобігти модифікації даних масиву?


9

Скажіть, у мене клас, який виглядає приблизно так (це лише приклад):

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

Як constу конструкторі копіювання, так і в doNotChangeMyDataфункції роблять це таким, що ptrйого неможливо змінити; однак це все ще дозволяє мені змінювати вміст масиву, на який вказує ptr.

Чи існує спосіб запобігти ptrзміні вмісту масиву 's constлише в екземплярах, окрім «обережності» (або відмови від вихідного покажчика)?

Я знаю, що міг би зробити щось подібне

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

Але я краще не повинен ...


1
ви могли скористатися astd::vector
idclev 463035818

std::vector::operator[]()Ви можете змінити значення, правильно?
marvinIsSacul

@ bivlyknownas_463035818 Відредаговане питання, так що це не варіант;) Це більше теоретичне запитання, але так, vectorспрацювало б.
ChrisMM

2
@marvinIsSacul впевнений, але std::vector::operator[]() constповертає constпосилання
idclev 463035818

@ChrisMM, що я очікував, просто хотів згадати про слона в кімнаті :)
idclev 463035818

Відповіді:


7

Покажчики не розповсюджуються const. Додавання constдо типу double*врожайності double* const, що призводить в НЕ - constзначення , коли разименовиваются.

Натомість ви можете використовувати 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
};

або вбудований масив (не рекомендується):

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

Усі три варіанти розповсюдження const.

Якщо ви дійсно хочете використовувати вказівники (настійно не рекомендується), принаймні використовуйте клавішу a, std::unique_ptrщоб уникнути ручного управління пам'яттю. Ви можете використовувати std::experimental::propagate_constобгортку з основ бібліотеки 2 ТС:

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

Це ще не в стандарті, але багато компіляторів підтримують його. Звичайно, такий підхід поступається правильним контейнерам.


намагаючись зробити це, не змінюючи основного типу даних, скоріше теоретичне питання, ніж щось інше. Якщо це неможливо, то я прийму це як не можливе.
ChrisMM

@ChrisMM Я оновив відповідь рішенням вказівника. Але чому :)
LF

"Чому" важко відповісти, більше цікавість. "Вбудований масив" або std::arrayне працює, якщо ви не знаєте розмір під час компіляції. vectorдодає накладні витрати; unique_ptrне додає накладні витрати, але якщо вказівник потрібно поділити, то вам потрібно shared_ptrдодати накладні витрати. Я не думаю, що VS на даний момент підтримує propagate_const(принаймні файл заголовка, на який посилається cppreference, не існує /std:c++latest) :(
ChrisMM

1
@ChrisMM Накладні витрати vectorчасто завищені TBH, особливо порівняно із зусиллями ручного управління пам'яттю. Крім того, якщо ви вказуєте загальні вказівки вручну, вам доведеться використовувати довідкові підрахунки, тому накладні витрати не властиві shared_ptr. Я не знав, що VS ще не підтримує propagate_const(GCC і Clang обидва підтримують його IIRC), але не важко розгорнути нашу власну відповідно до специфікації.
LF

Я погоджуюся, що накладні витрати мінімальні, але є причини використовувати необроблені покажчики, коли продуктивність є критичною (пам'ять та час). Я іноді використовую, а vectorпотім беру його вміст через .data()або &vec[0]замість цього, працюю безпосередньо з ним. У випадку спільного доступу у мене часто є один власник вказівника, який створює та видаляє, але інші класи діляться даними.
ChrisMM
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.