С++: правильность и аргументы указателя
Я понимаю, что указатель 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 делают pass-by-value для самого указателя, его невозможно, чтобы 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' arg, правильно?
Ответы
Ответ 1
У вас есть это в обратном порядке:
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
в любых других объявлениях функции.
Ответ 2
Ну, это не для вызывающего, а для кода внутри someFunc1
. Так что любой код внутри someFunc1
обычно не меняет его. как
void someFunc1(int *arg) {
int i = 9;
arg = &i; // here is the issue
int j = *arg;
}
Давайте рассмотрим некоторые примеры:
1) Просто сделав заостренное значение const
void someFunc1(const int * arg) {
int i = 9;
*arg = i; // <- compiler error as pointed value is const
}
2) Просто сделав указатель const
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
}
Это должно устранить все сомнения. Поэтому я уже упомянул, что он предназначен для кода функции, а не для вызывающего, и вы должны использовать наиболее ограничительные из 3 случаев, упомянутых выше.
EDIT:
- Даже в декларациях функций хорошая практика объявить
const
. Это не только повысит удобочитаемость, но также и вызывающий абонент узнает о контракте и имеет больше уверенности в неизменности аргументов. (Это требуется bcoz, вы, как правило, делитесь своими файлами заголовков, поэтому у вызывающего может не быть файла реализации c/cpp).
- Даже компилятор может указать лучше, если оба объявления и определения синхронизированы.
Ответ 3
У тебя своя логика неправильная. Вы должны прочитать тип назад, поэтому 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.
}
Ответ 4
Да, вы правы (игнорируя тот факт, что вы ошиблись в них) - нет смысла принимать параметры без ссылки const
. Кроме того, нет смысла возвращать значения без ссылки const
.
Ответ 5
У вас это неправильно:
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);
будет избыточным (и двусмысленным), хотя