Выделение указателя на константу

Пусть говорят, что у нас есть класс, называемый объектом.

int main(){
    object a;
    const object* b = &a;
    (*b); 
}

Вопрос: b - указатель на const, но объект, на который он указывает, на самом деле не является постоянным объектом. Мой вопрос заключается в том, когда мы разыскиваем указатель "b", почему мы получаем постоянный объект вместо того, что он на самом деле указывает на то, что это обычный не постоянный объект.

Ответ 1

Потому что, как встроенный оператор разыменования * работает в C++. Если вы разыщите указатель типа T *, вы получите lvalue типа T В вашем случае T является const object.

Ни оператор *, ни сам указатель заботится (или знает), что объект, на который указывает указатель, фактически непостоянен. Это просто не может быть другим способом в концепции статического набора текста, используемого языком C++.

Вся цель const-qualification на первом (и более глубоком) уровне косвенности - предоставить вам возможность создавать ограничительные пути доступа к объектам. Т.е. создавая указатель-на-const, вы намеренно и охотно мешаете себе (или кому-то еще) изменять мотив, даже если указатель не является постоянным.

Ответ 2

Тип выражения основан только на объявленных типах переменных в выражении, он не может зависеть от динамических данных времени выполнения. Например, вы можете написать:

object a1;
const object a2;
const object *b;
if (rand() % 2 == 0) {
    b = &a1;
} else {
    b = &a2;
}
(*b);

Тип *b определяется во время компиляции, он не может зависеть от того, что возвращает rand() при запуске программы.

Поскольку b объявляется как const object, то тип *b.

Ответ 3

Поскольку ключевое слово const означает, что вы не можете изменить этот объект, а не "объект не может быть изменен".

Это полезно, когда вы передаете объект какой-либо функции только для использования значения объекта, но не для его изменения:

// We expect PrintMyString to read the string
// and use its contents but not to modify it

void PrintMyString(const char *string);

void myfunction(int number)
{
    // build and print a string of stars with given length
    if(number>0 && number<=16]
    {
        char string[17];
        int i;
        for(i=0, i<number; i++)
            string[i] = '*';
        string[number] = '\0';

        PrintMyString(string);
    }
}

Функция PrintMyString получит массив символов, но массив будет передан как функция "только для чтения". Массив, безусловно, может быть изменен в рамках собственной функции, но PrintMyString может читать только то, что получает, а не изменять содержимое.

Ответ 4

const object* b = &a;

Это означает: b - указатель на объект const (только для чтения)

Ничего не говорит об const -ness a. Это просто означает, что b не имеет права изменять a.

Также известен как low-level const.


Вопреки вышеизложенному, верхний уровень const - это когда сам указатель является const.
object *const b = &a;   // b is a const pointer to an object

b = &some_other_object; // error - can't assign b to another object
                        // since b is a const pointer

*b = some_value;        // this is fine since object is non-const

Ответ 5

Здесь есть много других ответов, но я вынужден опубликовать это, потому что чувствую, что многие из них предоставляют слишком много деталей, блуждают по теме или предполагают знание жаргона C++, что ОП почти наверняка не имеет. Я думаю, что это бесполезно для OP и других людей на аналогичной стадии их карьеры, поэтому я попытаюсь прорвать часть этого.

Сама ситуация на самом деле очень проста. Декларация:

const SomeClassOrType *p = ...

просто говорит, что все, что указывает p, не может быть изменено с помощью этого указателя, и это полезно для обеспечения того, чтобы код, который получает доступ к этому объекту через p, не изменяет его, когда он не предполагается. Он чаще всего используется в объявлениях параметров, чтобы функции и методы знали, могут ли они модифицировать передаваемый объект (*).

В нем ничего не говорится о существовании того, что на самом деле указывается. Сам указатель, будучи просто простой душой, не несет с собой эту информацию. Он знает только, что в отношении указателя указываемый объект может быть прочитан, но не написан.

Конкретный пример (украденный у CiaPan):

void PrintString (const char *string);

char string [] = "abcde";
const char *p = string;
PrintString (p);

void PrintString (const char *ptr_to_string)
{
    ptr_to_string [0] = 0;   // oops!  but the compiler will catch this, even though the original string itself is writeable
}

Вы можете просто передать string в PrintString напрямую, конечно же, такую же разницу, потому что параметр также объявлен как const.

Другим способом взглянуть на это является то, что const представляет собой информацию, предоставленную программистом, чтобы помочь компилятору проверить правильность/последовательность кода во время компиляции. Это позволяет компилятору ловить ошибки, подобные выше, и, возможно, лучше оптимизировать. К тому времени, когда вы действительно запускаете свой код, это история.


(*) Современная идиома, вероятно, должна использовать константную ссылку, но я не хочу мутить воды, бросая это в основное обсуждение.

Ответ 6

Проще говоря,

  1. Для простых пуатеров результат унарного оператора * является ссылкой на указательный тип.

  2. Итак, если тип point-to имеет const-квалификацию, результат является ссылкой на константу.

  3. Ссылки на константы могут связываться с любыми объектами, даже с изменчивыми объектами другого типа. Когда вы привязываете constref к значению, вы обещаете не ad-hoc модифицировать это значение с помощью этого подтверждения (вы все равно можете сделать это явно, но приложите его к неконстантной ссылке).

  4. Аналогично, константные указатели могут указывать на объекты, которые по-прежнему могут быть изменены.

Ответ 7

Написав

const object* b = &a;

вы объявляете, что b является указателем (*) для object const типа, которому затем присваивается адрес a. a имеет тип object (но не const); вам разрешено использовать адрес неконстантного object вместо адреса const object.

Однако, когда вы разыскиваете * b, компилятор может перейти только в соответствии с вашим объявлением. Таким образом, *b является const object (однако вы все равно можете изменить a по своему усмотрению, поэтому остерегайтесь думать, что объект b указывает на то, что он не может измениться - он просто не может быть изменено посредством b)

Ответ 8

const object* b = &a;

б будет рассматривать то, что он указывает, как const, то есть он не может изменить a

object* const b = &a;

б сама const, то есть он не может указывать на другой object адрес, но он может изменить a

Ответ 9

Потому что во время выполнения это может быть объект const, что означает, что любые операции, выполняемые при разыменовании, должны быть совместимы с константой. Игнорируйте тот факт, что в вашем примере он был указан неконстантным объектом и не может указывать ни на что другое, язык не работает так.

Ответ 10

Вот конкретный пример того, почему так оно и есть. Допустим, вы заявляете:

int a[] = {1, 2, 3};
constexpr size_t n_a = sizeof(a)/sizeof(a[0]);

extern int sum( const int* sequence, size_t n );
sum_a = sum( a, n_a );

Теперь вы реализуете sum() в другом модуле. Он не знает, как был объявлен исходный объект, на который вы указывали. Можно было бы написать компилятор, который помечен указателями с этой информацией, но никакие компиляторы, используемые в настоящее время, по ряду причин.¹

Внутри sum(), который может быть в общей библиотеке, которая не может быть перекомпилирована с оптимизацией всей программы, все, что вы видите, является указателем на память, который не может быть изменен. Фактически, в некоторых реализациях попытка записи через указатель на const может привести к сбою программы с ошибкой защиты памяти. И способность переинтерпретировать блок памяти как некоторый другой тип важна для C/C++. Например, memset() или memcpy() переинтерпретирует его как массив произвольных байтов. Таким образом, реализация sum() не имеет возможности рассказать о происхождении аргумента указателя. Что касается его, то это просто указатель на const int[].

Что еще более важно, контракт функции говорит о том, что он не собирается изменять объект через этот указатель. Он мог бы просто отбросить спецификатор const явно, но это будет логическая ошибка. Если вы объявляете указатель const, как будто бы задействованы безопасность вашего пистолета: вы хотите, чтобы компилятор не дал вам стрелять в ногу.

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

² С педантичным исключением mutable членов данных.