Комментарии 34
Очень напрягает, когда в библиотеках по сути const-методы таковыми не объявлены. Мешает реализовывать собственную const-логику. Например:
А если объявить MyContainer::process() как не-const, то его уж нельзя будет использовать в других const-методах. И никак этот не-const count() не завернёшь в const-метод. Конечно, можно попытаться реализовать собственный метод вроде myCount() и объявить его как const, но это, конечно, не всегда возможно.
// Библиотека class Container { public: unsigned count (); // не const }; // MyModule.cpp class MyContainer : public Container { public: void process () const { for (unsigned i = 0; i < count (); ++i) // Нельзя использовать не-const count() {...} } };
А если объявить MyContainer::process() как не-const, то его уж нельзя будет использовать в других const-методах. И никак этот не-const count() не завернёшь в const-метод. Конечно, можно попытаться реализовать собственный метод вроде myCount() и объявить его как const, но это, конечно, не всегда возможно.
+2
НЛО прилетело и опубликовало эту надпись здесь
Что же делать, если хочется последовательно, а библиотека вот не даёт?
0
Это обходиться прри помощи адартера обьекта.
В вашем случае:
// Вообщето православный адартер обьекта должен работать с ссылкой или указателем на адаптируемый обьект, ну да ладно
class ContainerAdapter
{
public:
unsigned count () const {return cont_.count ()}
private:
mutable Container cont_;
};
class MyContainer: public ContainerAdapter
{
//…
};
В вашем случае:
// Вообщето православный адартер обьекта должен работать с ссылкой или указателем на адаптируемый обьект, ну да ладно
class ContainerAdapter
{
public:
unsigned count () const {return cont_.count ()}
private:
mutable Container cont_;
};
class MyContainer: public ContainerAdapter
{
//…
};
+5
Действительно, сам не додумался :) Спасибо!
0
Можно проще
// Библиотека
class Container
{
public:
unsigned count (); // не const
};
// MyModule.cpp
class MyContainer: protected Container
{
public:
unsigned count() const
{
return Container::count();
}
// Ну и еще что нужно, соответственно, открываем
};
// Библиотека
class Container
{
public:
unsigned count (); // не const
};
// MyModule.cpp
class MyContainer: protected Container
{
public:
unsigned count() const
{
return Container::count();
}
// Ну и еще что нужно, соответственно, открываем
};
0
Не даст вызвать не-const Container::count() из Container::count() const.
Есть вот такой вариант:
Есть вот такой вариант:
// Библиотека class Container { public: unsigned count (); // не const }; // MyModule.cpp class MyContainer : public Container { public: void process () const { for (unsigned i = 0; i < (const_cast<MyContainer*>(this))->count (); // Приводим this к не-const типу ++i) {...} } };
0
Тогда уж вот так:
((Container*)(const_cast<MyContainer*>(this)))->count()
А вообще, убивая вот так просто слово const, Вы нарушаете гарантию, данную этим словом, что «ни один из членов класса не претерпит каких-либо изменений».
((Container*)(const_cast<MyContainer*>(this)))->count()
А вообще, убивая вот так просто слово const, Вы нарушаете гарантию, данную этим словом, что «ни один из членов класса не претерпит каких-либо изменений».
0
1. Зачем преобразование к Container*?
2. Согласен насчёт нарушения гарантий, но что же делать, если библиотека не даёт гарантий там, где должна бы? Конечно, не однозначный вопрос, что должна библиотека, а что нет. Однако, const-корректность библиотеки делает её более удобной. Короче говоря, лучше уж аккуратно сделать const_cast в одном изолированном месте, чем отказываться от ключевого слова const вообще.
2. Согласен насчёт нарушения гарантий, но что же делать, если библиотека не даёт гарантий там, где должна бы? Конечно, не однозначный вопрос, что должна библиотека, а что нет. Однако, const-корректность библиотеки делает её более удобной. Короче говоря, лучше уж аккуратно сделать const_cast в одном изолированном месте, чем отказываться от ключевого слова const вообще.
+2
1. Поменяйте название process на count и сразу поймете зачем. Это преобразование, в конечном счете, не сделает код более медленным или громоздким, а вот избежать ошибок и ввести лишнюю ясность поможет.
2. Я тоже предпочел бы сделать так, хоть и считаю это менее правильным. Дилемма :)
2. Я тоже предпочел бы сделать так, хоть и считаю это менее правильным. Дилемма :)
0
В дополнение к вышесказанному — не используйте c-style cast.
0
Это тоже самое, что Blackened привел в самом нечале. Нельзя вызвать не-конст ф-цию из конст.
+3
Сделать const_cast на this. Не очень красиво, но работает.
0
класс. не писал на c++ со времен учебы.
но все равно интересно!
но все равно интересно!
0
НЛО прилетело и опубликовало эту надпись здесь
Такую фотку надо вешать на книги, типа «С++ для чайников»))))
0
Прописная истина же.
И, я дак, например, черту не провожу, читаю как есть, так же как в случае с массивом указателей (int *a[]) или указателем на массив (int (*a)[]).
const char * a = «1»;
1) идентификатор а объявлен как
2) указатель на
3) значение const char.
char * const a = «1»;
1) идентификатор а объявлен как
2) const указатель на
3) значение char.
И, я дак, например, черту не провожу, читаю как есть, так же как в случае с массивом указателей (int *a[]) или указателем на массив (int (*a)[]).
const char * a = «1»;
1) идентификатор а объявлен как
2) указатель на
3) значение const char.
char * const a = «1»;
1) идентификатор а объявлен как
2) const указатель на
3) значение char.
+3
а еще можно делать так
class A
{
void f(int);
};
void A::f(const int);
так как параметр передается по значению то такой код корректен. В результате мы получаем простую и легкочитаемую запись в заголовке — а в теле функции страхуем себя от случайного изменения значения переменной.
Далее.
Если вы хотите объявить const поле класса, то инициализировать его обязательно списком инициализации ( так же как и ссылки )
class B
{
const int constValue;
public:
B(int&);
};
B::B()
:constValue(33)
{}
в противном случае если вы попробете сделать это в теле конструктора — получите ошибку.
В данном случае случше лучше всего руководствоваться следующим примером
аналог инициализации в теле конструктора ( между {} )
int x;
x=5;
аналог инициализации списоком инициализации ( после :)
int x = 5;
именно поэтому для инициализации констант и ссылок в качестве полей класса подходит только список инициализации
class A
{
void f(int);
};
void A::f(const int);
так как параметр передается по значению то такой код корректен. В результате мы получаем простую и легкочитаемую запись в заголовке — а в теле функции страхуем себя от случайного изменения значения переменной.
Далее.
Если вы хотите объявить const поле класса, то инициализировать его обязательно списком инициализации ( так же как и ссылки )
class B
{
const int constValue;
public:
B(int&);
};
B::B()
:constValue(33)
{}
в противном случае если вы попробете сделать это в теле конструктора — получите ошибку.
В данном случае случше лучше всего руководствоваться следующим примером
аналог инициализации в теле конструктора ( между {} )
int x;
x=5;
аналог инициализации списоком инициализации ( после :)
int x = 5;
именно поэтому для инициализации констант и ссылок в качестве полей класса подходит только список инициализации
0
...«Если вы в этом случае не реализуете не-const-методы, то во всех случаях будут молча использоваться const-методы.»…
непонятно…
непонятно…
0
> const int i(1);
> int const j(1);
>
> Все они правильные и делают одно и тоже — создают переменную,
> значение которой изменить нельзя.
Что это за переменные такие i(1) и j(1)? Просветите плиз, что это означает.
> int const j(1);
>
> Все они правильные и делают одно и тоже — создают переменную,
> значение которой изменить нельзя.
Что это за переменные такие i(1) и j(1)? Просветите плиз, что это означает.
0
Переменные называются i и j, а
int i(1);
это определение переменной i типа int и инициализация её значением 1. Читайте 8.5/1.0
Мда ну и синтаксис…
Зачем стандатр позволяет такие запутывающие конструкции? Ведь по синтаксису написание аналогично вызову функции.
Зачем стандатр позволяет такие запутывающие конструкции? Ведь по синтаксису написание аналогично вызову функции.
0
class A { public: A(int i) {} }; class B { public: B(int i, int j) {} }; int main() { A a1 = 1; A a2(2); int i1 = 1; int i2(2); B b1(1, 2); }
Я доступно объяснил, или нужно прокомментировать?
0
Честнагря нихрена не понял. Тем более не увидел ответа на вопрос — зачем логическую конструкцию с присваиванием писать через int i2(2) вместо int i2=2?
0
Объясняю.
По идеологии С++ пользовательские типы не должны в использовании отличаться от встроенных. Для встроенных типов разрешена инициализация вида
По идеологии С++ пользовательские типы не должны в использовании отличаться от встроенных. Для встроенных типов разрешена инициализация вида
Type t = Initializer;
, поэтому такую же инициализацию разрешили для пользовательских типов, если есть конструктор, принимающий один аргумент (см. класс A). Для пользовательских типов инициализация вида Type t(InitializerList);
универсальна (см. классы A и B), поэтому для однообразности есть возможность инициализировать точно так же и экземпляры встроенных типов.0
Это инициализация этих переменных единицами. То же самое, что и const int i = 1; int const j = 1;
0
>Существует мнемоническое правило, позволяющее легко запомнить, к чему относится const. Надо провести черту через "*", если const слева, то оно относится к значению данных; если справа — к значению указателя.
Еще лучше мнемоническое правило было написано у Бочкова и Субботина: «изнутри наружу». Все, что участвует в создании типа объявляемой сущности: const, volatile, *, (), [] читается от имени этой сущности (самая внутренность) — наружу (то есть влево для префиксов и вправо для постфиксов). Постфиксы () [] имют приоритет перед префиксами const volatile *. Приоритет может быть изменен скобками.
В частности:
pch(0) — это указатель(1) на const(2)
pch(0) — это константный(1) указатель(2)
Еще лучше мнемоническое правило было написано у Бочкова и Субботина: «изнутри наружу». Все, что участвует в создании типа объявляемой сущности: const, volatile, *, (), [] читается от имени этой сущности (самая внутренность) — наружу (то есть влево для префиксов и вправо для постфиксов). Постфиксы () [] имют приоритет перед префиксами const volatile *. Приоритет может быть изменен скобками.
В частности:
char const *pch; (2) (1)(0) <------------
pch(0) — это указатель(1) на const(2)
char* const cpch; (2) (1) (0) <------------
pch(0) — это константный(1) указатель(2)
0
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Многоликий const