C++ постоянный вопрос
Если я сделаю это:
// 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
7 ответов
Ключевое слово 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 передается по значению, постоянство не имеет значения.
Во втором примере вы говорите, что ваша функция принимает указатель на один тип, но затем реализуете его, как указатель на другой тип, поэтому происходит сбой.
Во-первых, вы пообещали компилятору, но не другим пользователям класса, что вы не будете редактировать переменную.
Во втором примере вы пообещали другим пользователям класса, что не будете редактировать их переменные, но не смогли выполнить это обещание.
Я должен также отметить, что есть четкое различие между
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) {}
без проблем. Вы сказали своим пользователям, что вы можете редактировать их переменные. В своей реализации вы сказали компилятору, что эта конкретная реализация не будет редактировать эти переменные, даже если вы сказали пользователям, что вы можете. Вы не нарушили обещание для пользователя, и поэтому код компилируется.
Смотрите этот вопрос, этот вопрос и этот вопрос.
По сути, const означает только то, что функция не будет изменять значение указателя. Содержимое указателей не является константой, так же как и подпись заголовка.
В первом const
не влияет на интерфейс, только на реализацию. Вы говорите компилятору: "Я не собираюсь менять значение bar*
внутри этой функции ". Вы все еще можете изменить то, на что указывает указатель. В последнем случае вы говорите компилятору (и всем вызывающим сторонам), что вы не будете изменять bar
структура, которая bar*
указывает на.
Это, вероятно, не заботится о void Foo::foo(bar* const pBar)
потому что то, как вы относитесь к самому указателю (постоянному или нет), не имеет значения ни на один бит вне подпрограммы. Правила Си говорят, что никакие изменения в pBar не будут выходить за пределы foo в любом случае.
Однако, если это (const bar* pBar)
, это имеет значение, потому что это означает, что компилятор не должен позволять вызывающим объектам передавать указатели на неконстантные объекты.
Итак, второе const в:
void Foo::foo(const bar* const);
Не является ли часть сигнатуры метода?
Это проще понять с помощью типа переменной, отличного от указателя. Например, у вас может быть следующее объявление функции:
void foo( int i );
Определение может выглядеть так:
void foo( const int i ) { ... }
Независимо от того, является ли переменная "i" постоянной или нет на стороне определения, это деталь реализации. Это не влияет на клиентов этой функции.