C ++: Const-корректность и аргументы-указатели - PullRequest
20 голосов
/ 10 января 2012

Я понимаю, что указатель const может быть объявлен несколькими способами:

const int * intPtr1; // Declares a pointer that cannot be changed.
int * const intPtr2; // Declares a pointer whose contents cannot be changed.

// EDIT: THE ABOVE CLAIMS ARE INCORRECT, PLEASE READ THE ANSWERS.

Но как насчет тех же принципов в контексте аргументов функции?

Я бы предположил, что следующееизбыточно:

void someFunc1(const int * arg);
void someFunc2(int * arg);

Так как someFunc 1 и 2 выполняют передачу по значению для самого указателя, для someFunc1 невозможно изменить значение исходного указателя при данном вызове функции.Чтобы проиллюстрировать:

int i = 5;
int * iPtr = &i;

someFunc1(iPtr); // The value of iPtr is copied in and thus cannot be changed by someFunc1.

Если это так, то нет смысла когда-либо объявлять функцию с аргументом типа 'const int * ptr', правильно?

Ответы [ 5 ]

31 голосов
/ 10 января 2012

У вас это задом наперед:

const int * intPtr1; // Declares a pointer whose contents cannot be changed.
int * const intPtr2; // Declares a pointer that cannot be changed.

Следующее const действительно не нужно, и нет никакой причины помещать его в функцию объявление :

void someFunc1(int * const arg);

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

24 голосов
/ 10 января 2012

Ну, это не для звонящего, а для кода внутри someFunc1.Так что любой код внутри someFunc1 не изменит его случайно.как

void someFunc1(int *arg) {
  int i = 9;
  arg = &i;  // here is the issue
  int j = *arg;
}

Давайте рассмотрим конкретный пример:

1) Просто сделаем указанное значение постоянным

void someFunc1(const int * arg) {
int i = 9;
*arg = i; // <- compiler error as pointed value is const
}

2) Просто сделаем указатель постоянным

void someFunc1(int * const arg) {
int i = 9;
arg = &i; // <- compiler error as pointer is const
}

3) Правильный способ использования const, если задействованные переменные могут быть const:

void someFunc1(const int * const arg) {
    int i = 9;
    *arg = i; // <- compiler error as pointed value is const
    arg = &i; // <- compiler error as pointer is const
}

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

РЕДАКТИРОВАТЬ:

  • Даже вобъявления функций это хорошая практика объявлять const.Это не только повысит удобочитаемость, но и вызывающий абонент будет осведомлен о контракте и будет более уверен в неизменности аргументов.(Это необходимо, потому что вы обычно делитесь своими заголовочными файлами, чтобы у вызывающей стороны не было вашего файла реализации c / cpp)
  • Даже компилятор может указать лучше, если и объявление, и определения синхронизированы.
6 голосов
/ 10 января 2012

Вы неправильно поняли свою логику. Вы должны читать тип в обратном порядке, поэтому const int * - это указатель на const int, а int * const - это const указатель на int.

Пример:

void foo() {
    int a = 0;
    int b = 0;

    int * const ptrA = &a;
    *ptrA = 1;
    ptrA = &b; ///< Error

    const int * ptrB = &a;
    *ptrB = 1; ///< Error
    ptrB = &b;

    const int * const ptrC = &a;
    *ptrC = 1; ///< Error
    ptrC = &a; ///< Error
}

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

void someFunc1(const int * arg) {
    // Can't change *arg in here
}

void someFunc2(int * arg) {
    *arg = 5;
}

void foo() {
    int a = 0;
    someFunc1(&a);
    someFunc2(&a);

    const int b = 0;
    someFunc1(&b);
    someFunc2(&b); ///< *** Error here. Must pass in an int not a const int.
}
2 голосов
/ 10 января 2012

Вы ошиблись:

const int * intPtr1; // Declares a pointer whose contents cannot be changed.
int * const intPtr2; // Declares a pointer that cannot be changed.

Вообще говоря, при написании этого выражения немного проще рассуждать о константности: const int* того же типа, что и int const *. В этой записи правила намного понятнее, const всегда применяется к предшествующему типу, поэтому:

int const * intPtr1; // Declares a pointer to const int.
int * const intPtr2; // Declares a const pointer to int.
int const * * const * complexPtr; // A pointer to const pointer to pointer to const int

Когда тип записывается с начальным const, const обрабатывается так, как если бы он был написан после первого типа, поэтому const T* становится T const *.

void someFunc2(int * arg);

Поэтому не является избыточным, поскольку someFunc2 может изменить содержимое arg, а someFunc1 - нет. void someFunc3(int * const arg); будет избыточным (и неоднозначным), хотя

2 голосов
/ 10 января 2012

Да, вы правы (игнорируя тот факт, что вы их неправильно поняли) - нет смысла принимать нереферентные const параметры. Кроме того, нет смысла возвращать нереферентные значения const.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...