
![]() |
Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
|
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[216.73.217.2] |
![]() |
|
![]() |
|
|
Часто в исходниках видел перед методами служебное слово explicit, скажите, пожалуйста, зачем оно нужно и что оно делает?
|
Сообщ.
#2
,
|
|
|
Запрещает неявное преобразование
Используется в конструкторах с одним параметром. |
Сообщ.
#3
,
|
|
|
Цитата Крон @ Используется в конструкторах с одним парамтером. При чём тут один параметр? Используется для того, чтобы конструктор задавали явно. |
Сообщ.
#4
,
|
|
|
А как можно неявно задать конструктор с более чем один параметром?
|
Сообщ.
#5
,
|
|
|
Цитата Крон @ А как можно неявно задать конструктор с более чем один параметром? ![]() ![]() class A { A(int first, int second); ~A(); }; |
![]() |
Сообщ.
#6
,
|
|
Цитата Алкаш @ Используется для того, чтобы конструктор задавали явно. Ок, а что такое по твоему "задать конструктор"? ![]() DelphiLexx, ![]() ![]() struct A { A(int) { } }; struct B { explicit B(int) { } }; int main() { A a1(5); // ok B b1(5); // ok A a2 = 10; // ok B b2 = 10; // error <<<========================== } |
Сообщ.
#7
,
|
|
|
Цитата archimed7592 @ Ок, а что такое по твоему "задать конструктор"? ![]() Цитата archimed7592 @ B b1(5); // ok |
Сообщ.
#8
,
|
|
|
Цитата Алкаш @ Цитата Крон @ Используется в конструкторах с одним парамтером. При чём тут один параметр? Используется для того, чтобы конструктор задавали явно. Интересно а для чего использовать тогда explicit c конструктором который имеет более чем один параметр? Я просто не вижу смысла потому что приведение и так неявное невозможно. А archimed7592 уже ответил на вопрос полностъю я считаю. |
Сообщ.
#9
,
|
|
|
Цитата Craft @ Интересно а для чего использовать тогда explicit c конструктором который имеет более чем один параметр? ![]() ![]() struct S { /*explicit*/ S(int, int = 0) {} }; int main() { S s = 10; } раскомментируй explicit |
Сообщ.
#10
,
|
|
|
![]() ![]() class Foo { public: Foo(const std::string & sString, bool Flag = true); Foo(bool Flag); ... }; Вот пример такой класс, а теперь прикол : ![]() ![]() Foo("foo"); Вызывется 2-ой конструктор, а не первый. Понятно что есть небольшая ошибка, что ведь нужен обьект типа string, а я передаю const char * - это простая ошибка которая может возникнуть в процессе кодирования, когда нету autocomplete и программист уже устал. Если сделать оба конструктора explicit, такой ошибки не будет и компилятор предупредит об этой ошибки с неявным преобразованием. На эту ошибку наткнулся в реальном коммерческом проекте. Вывод : всегда делать конструкторы explicit, если у него есть параметры. |
Сообщ.
#11
,
|
|
|
Alek86 Я не имел ввиду что конструктор принимает значения по умолчанию. Так можна и 10 аргументов инициализировать по умолчанию. Ты же инициализуеш неявно только первый аргумент, а остальныи присваиваеш значение по умолчанию. Я знаю как работает explicit и для чего он нужен.
Допустим без explicit у тебя конструктор автоматически определил бы преобразования из int в твой клас S, ну создаст он стек на 10 елементов. Да не спорю это не то что мы ожидаем. Но первая переменная у тебя всё равно получит значение 10. Так работает неявное преобразование. Большой минус только что стек сильно увеличивается. Это неправильный стиль я не спорю. Но чтоб считать это такой уж ошибкой я не вижу смысла. Программист должен знать что он делает в первую очередь. Пусть хоть на миллион элементов неявно инициализацию использует. Лиш бы ему памъяти хватило чтоб небыло переполнения. Просто насколько мне не изменяет памъять то у windows под кучю выделяется 1Мб так что если не жалко израсходованой памъяти то можна такой метод использовать. Я никогда не видел что ктото использовал неявное преобразование на подобие этого ![]() ![]() S s = 10; ![]() P.S. Если чесно, то я не подумал, когда писал ответ, про возможность передачи значения по умолчанию. ![]() ![]() Cechmanek ![]() ![]() Foo("foo"); Разве что через такую ![]() ![]() Foo p = "foo"; Цитата B b1(5); // ok |
Сообщ.
#12
,
|
|
|
Цитата Cechmanek @ Вызывется 2-ой конструктор, а не первый. Это потому что последовательность преобразований из массива char в bool - стандартная, а в std::string - пользовательская. При разрешении перегрузки предпочтение отдаётся последовательности стандартных преобразований. Цитата Cechmanek @ Если сделать оба конструктора explicit то в этом плане ничего не изменится. Цитата Cechmanek @ Вывод : всегда делать конструкторы explicit, если у него есть параметры. Плохой вывод. |
Сообщ.
#13
,
|
|
|
Один из способов из способов избежать создания временных объектов это по возможности использовать объявления конструкторов как
![]() ![]() explicit ![]() Вот и все. ![]() |
Сообщ.
#14
,
|
|
|
Цитата Craft @ Начинающий программист в основном не использует explicit, а опытный программист просто напросто никогда не напишет такой код который я написал више. ты считаешь, что опытный программист - это безошибочная машина для написания кода? огорчяу тебя - даже "сеньоры" с огромным стажем работы ошибаются и достаточно нередко - специфика работы такая, что всего умом не охватишь (у макконелла написано как от этого максимально откреститься (путем упрощения кода) но на практике так круто никто не пишет ![]() потому и придуманы специальные "ограничители", чтобы ошибьки не так плодились. типа приватных членов класса, приватного наследования, explicit, попытались добавить спецификацию throw функции и т.п. и потому же рекомендуют пореже писать кастомных операторов преобразования насчет правила "ставить explicit всегда и везде" не согласен, но только потому что не обжигался на этом "в реале" ![]() я ставлю его только тогда, когда "чувствую" что тут его лучше поставить |
Сообщ.
#15
,
|
|
|
Craft и Archimed раскрыли тему полностью
![]() Цитата Один из способов из способов избежать создания временных объектов это по возможности использовать объявления конструкторов как временные объекты здесь вообще не при чём. |
Сообщ.
#16
,
|
|
|
Цитата Craft @ Просто насколько мне не изменяет памъять то у windows под кучю выделяется 1Мб Жжёшь ![]() Цитата Craft @ Я никогда не видел что ктото использовал неявное преобразование на подобие этого ![]() ![]() S s = 10; Тебя послушать, так шаблон std::complex создан на коленках начинающими программистами ![]() |
![]() |
Сообщ.
#17
,
|
|
Цитата vovan01 @ Один из способов из способов избежать создания временных объектов это по возможности использовать объявления конструкторов как Глупость. |
Сообщ.
#18
,
|
|
|
Цитата Бобёр @ Craft и Archimed раскрыли тему полностью ![]() Цитата Один из способов из способов избежать создания временных объектов это по возможности использовать объявления конструкторов как временные объекты здесь вообще не при чём. лучше почитай литературу, могу дать ссылку на книжку ![]() а это другой сучий когда можно использовать explicit As a rule, every constructor that takes a single argument, including constructors that take multiple arguments with default values such as the following, should be explicit, unless you have a good reason to allow implicit conversions: ![]() ![]() class File { public: //this cоnstructor may be called with a single argument //it's therefore declared explicit: explicit File(const char *name, ios_base::openmode mode=ios_base::out, long protection = 0666); }; |
Сообщ.
#19
,
|
|
|
и где тут встечаются слова "временные объекты"?
тут видны слова "implicit conversions" - о чем все и говорят |
![]() |
Сообщ.
#20
,
|
|
Цитата vovan01 @ лучше почитай литературу, могу дать ссылку на книжку ![]() Редко встречаю такой аргумент, но всегда ему умиляюсь ![]() |
Сообщ.
#21
,
|
|
|
предлагаю с компилить следующий код
![]() ![]() #include <cstdlib> #include <iostream> using namespace std; template<typename T > class ObjectBase { public : ObjectBase(){ cout <<"In constructor_1"<<endl; } ObjectBase(T ob): i(ob){ cout <<"In constructor_2"<<endl; } void GetId(){ cout<< i <<endl; } ~ObjectBase(){ cout <<"In destructor"<<endl; } private: T i; }; template<typename U> void f( ObjectBase<U> const &o) { cout <<"In funstion f"<<endl; } int main(int argc, char *argv[]) { cout<<"In main"<<endl; ObjectBase<int> obj_1; obj_1 = 10; obj_1.GetId(); f(ObjectBase<int>()); system("PAUSE"); return EXIT_SUCCESS; } а после этого по пробуйте вот это ![]() ![]() #include <cstdlib> #include <iostream> using namespace std; template<typename T > class ObjectBase { public : explicit ObjectBase(){ cout <<"In constructor_1"<<endl; } explicit ObjectBase(T ob): i(ob){ cout <<"In constructor_2"<<endl; } void GetId(){ cout<< i <<endl; } ~ObjectBase(){ cout <<"In destructor"<<endl; } private: T i; }; template<typename U> void f( ObjectBase<U> const &o) { cout <<"In funstion f"<<endl; } int main(int argc, char *argv[]) { cout<<"In main"<<endl; ObjectBase<int> obj_1(10); obj_1.GetId(); f(ObjectBase<int>()); system("PAUSE"); return EXIT_SUCCESS; } Теперь сравните результаты думаю что будет ясно что преобразование ![]() ![]() obj_1 = 10; создаёт временный объект. ![]() |
![]() |
Сообщ.
#22
,
|
|
vovan01, а ты попробуй во втором случае убрать explicit и скомпилировать, ага?
С тем же успехом можно было в первом варианте написать return 0, а во втором - return EXIT_SUCCESS и заявить, что EXIT_SUCCESS помогает избежать временных объектов. explicit, как и EXIT_SUCCESS к временным объектам никакого отношения не имеет. |
Сообщ.
#23
,
|
|
|
Цитата archimed7592 @ vovan01, а ты попробуй во втором случае убрать explicit и скомпилировать, ага? С тем же успехом можно было в первом варианте написать return 0, а во втором - return EXIT_SUCCESS и заявить, что EXIT_SUCCESS помогает избежать временных объектов. explicit, как и EXIT_SUCCESS к временным объектам никакого отношения не имеет. ты просто не понял ничего если оставить линию ![]() ![]() obj_1 = 10; то по просту у тебя во втором случае компилятор выдаст ошибку по этому я и написал. ![]() ![]() ObjectBase<int> obj_1(10); archimed7592 - так ты убедился что в первом случае создаётся временный объект? |
![]() |
Сообщ.
#24
,
|
|
Цитата vovan01 @ ты просто не понял ничего Куда уж мне ![]() Цитата vovan01 @ если оставить линию obj_1 = 10; то по просту у тебя во втором сучий компилятор выдаст ошибку по этому я и написал. ObjectBase<int> obj_1(10); Вы делаете мне смешно ![]() Почему ты не заменил эту строку на return 0; ? Из твоих слов получается, что присваивание и конструирование - это одно и то же. Что ж, тогда я заявляю, что присваивание и return 0 - это одно и то же ![]() Если класс подразумевает операцию присваивания, то тебе придётся её реализовать и explicit никак на это не повлияет. Если ты захочешь реализовать эту операцию "без временных объектов" - ты её реализуешь. Не захочешь - будут временные объекты(или их не будет - это уже компилятору виднее, стандарт ему руки развязывает). При чём здесь explicit? Класс с операцией присваивания int и класс без таковой операции - это два разных класса, не надо подавать их как один и тот же класс с разным выходом временных объеков. Цитата vovan01 @ archimed7592 - так ты убедился что в первом случай создаётся временный объект? Делать мне больше нечего. |
Сообщ.
#25
,
|
|
|
archimed7592
Цитата archimed7592 @ Почему ты не заменил эту строку на return 0; ? я тебе про Фому а ты мне про Ерему. Слушай я прогу написал в IDE Dev-C++, если ты слыхал про такую, там EXIT_SUCCESS = 0 |
Сообщ.
#26
,
|
|
|
vovan01, сравни:
![]() ![]() ObjectBase<int> obj_1; obj_1 = 10; и ![]() ![]() ObjectBase<int> obj_1(10); Мне почему-то не кажется, что это одно и то же. И при чем тут explicit? ЗЫ Да и копировщик добавить бы не мешало |
Сообщ.
#27
,
|
|
|
Затянувшаяся жара определённо плохо влияет на некоторых людей.
|
Сообщ.
#28
,
|
|
|
Цитата pan2004 @ Мне почему-то не кажется, что это одно и то же. И при чем тут explicit? ЗЫ Да и копировщик добавить бы не мешало Ну, автор примера имел в виду, что в первом случае для присваивания будет сначала создан временный объект (путем вызова соответствующего конструктора), после чего будет собственно присваивание. А во втором случае, если просто добавить explicit, то присваивание не пройдет: ![]() ![]() hellow_world.cpp:37: error: no match for 'operator=' in 'obj_1 = 10' hellow_world.cpp:7: note: candidates are: ObjectBase<int>& ObjectBase<int>::operator=(const ObjectBase<int>&) т. к. не будет выполнено неявное приведение 10-ти к ObjectBase. |