С ++ const вопрос

Если я сделаю это:

// In header 
class Foo {
void foo(bar*);
};

// In cpp
void Foo::foo(bar* const pBar) {
//Stuff
}

Компилятор не жалуется, что подписи для Foo :: foo не совпадают. Однако если бы у меня было:

void foo(const bar*); //In header
void Foo::foo(bar*) {} //In cpp

Код не скомпилируется.

Что происходит? Я использую gcc 4.1.x

Вы хотели поместить константу с другой стороны от * во втором примере? Некоторые люди отвечают, объясняя разницу в значении этого, а другие отвечают, объясняя разницу в том, находится ли константа в файле cpp или h.

Scott Langham 06.11.2008 22:45

Вам следует выбрать ответ на свой вопрос или обновить, если это не тот ответ, который вы ищете.

Douglas Mayle 07.11.2008 20:02
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
8
2
2 064
7
Перейти к ответу Данный вопрос помечен как решенный

Ответы 7

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

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

Следует также отметить, что между

bar* const variable

и

const bar* variable

и

const bar* const variable

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

Чтобы добавить немного больше пояснений к заданному вопросу, вы всегда можете пообещать БОЛЬШЕ const, чем меньше. Учитывая класс:

class Foo {
    void func1 (int x);
    void func2 (int *x);
}

Вы можете скомпилировать следующую реализацию:

Foo::func1(const int x) {}
Foo::func2(const int *x) {}

или же:

Foo::func1(const int x) {}
Foo::func2(const int* const x) {}

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

Я не думаю, что ваша теория верна: 1) Если вы объявляете «int *» и реализуете «const int *», как в вашем примере, он не компилируется. 2) Если вы передаете по значению, вы можете объявить const, а не реализовать с помощью const. Когда вы переходите по значению, это не имеет значения.

Dusty Campbell 06.11.2008 23:23

да. я согласен, вы ошибаетесь. void f (int const *); отличается от void f (int *); я думаю, вы хотели написать void f (int * const); имеет ту же сигнатуру, что и void f (int *); вам следует изменить свой пример, так как он может запутать новичков.

Johannes Schaub - litb 07.11.2008 09:10

В первом случае const влияет не на интерфейс, а только на реализацию. Вы говорите компилятору: «Я не собираюсь изменять значение bar* в этой функции». Вы все еще можете изменить то, на что указывает указатель. В последнем случае вы сообщаете компилятору (и всем вызывающим абонентам), что вы не будете изменять структуру bar, которую представляет собой bar*указывает на.

Я думаю, вы имеете в виду в последнем предложении «вы не будете изменять полосу, на которую указывает полоса *». «Изменить то, на что указывает полоса *» может означать «присвоить полосе * новое значение», что, конечно, нормально во втором примере.

Steve Jessop 07.11.2008 00:20

Спасибо за это, исправлено. Местоимения - проклятие ясности.

Greg Hewgill 07.11.2008 04:01

См. этот вопрос, этот вопрос и этот вопрос.

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

Итак, вторая константа в:

void Foo::foo(const bar* const);

Является ли нет частью сигнатуры метода?

Вы объявляете метод в файле .h, вы определяете его в .cpp. Все в объявлении является частью сигнатуры (обе константы), похоже, что в определении вы можете более точно уточнить тип аргумента, если это определение не нарушает семантику объявления.

Scott Langham 06.11.2008 22:18

@mamin: Верно. Например, посмотрите на ошибку компоновщика, которую вы получаете, если объявляете (но не определяете) функцию «void foo (const int * const a)», а затем вызываете ее. GCC сообщил об отсутствии подписи "foo (int const *)".

Steve Jessop 07.11.2008 00:24

просто: если вы добавите / удалите const, он не будет частью подписи. так что void foo (intconst); и void foo (int); действительно имеют такую ​​же подпись. НО void foo (int const *); и void foo (int *); имеют разные сигнатуры: к типу параметра добавляется не константа, а к указанному типу.

Johannes Schaub - litb 08.11.2008 18:39
Ответ принят как подходящий

Ключевое слово const в первом примере бессмысленно. Вы говорите, что не планируете менять указатель. Однако указатель был передан по значению, поэтому не имеет значения, измените вы его или нет; это не повлияет на вызывающего абонента. Точно так же вы также можете сделать это:

// In header 
class Foo {
void foo( int b );
};

// In cpp
void Foo::foo( const int b ) {
//Stuff
}

Вы даже можете сделать это:

// In header 
class Foo {
void foo( const int b );
};

// In cpp
void Foo::foo( int b ) {
//Stuff
}

Поскольку int передается по значению, постоянство не имеет значения.

Во втором примере вы говорите, что ваша функция принимает указатель на один тип, но затем реализует ее как указатель на другой тип, поэтому она терпит неудачу.

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

void foo( int i );

Определение может выглядеть так:

void foo( const int i ) { ... }

Независимо от того, является ли переменная 'i' константой на стороне определения, является деталью реализации. Это не влияет на клиентов этой функции.

Я не понимаю, как голосуют пользователи SO. наверху проголосовало +15 выше, это ответ, в конце которого неверное утверждение, и он не хочет его исправлять (человек сказал ему это 3 месяца назад), а на -1 был этот ответ, который все в порядке и объяснен намного лучше (ИМХО). делаю +1 ради справедливости.

Johannes Schaub - litb 01.04.2009 19:57

Вероятно, он не особо заботится о void Foo::foo(bar* const pBar), потому что то, как вы относитесь к самому указателю (константа или нет), не имеет значения вне подпрограммы. Правила C говорят, что никакие изменения в pBar в любом случае не будут выходить за пределы foo.

Однако, если это (const bar* pBar), это имеет значение, потому что это означает, что компилятор не разрешает вызывающим объектам передавать указатели на неконстантные объекты.

Другие вопросы по теме