Как при создании объекта класса вызвать функцию, которую в дальнейшем нельзя будет вызывать (ни с помощью данного экземпляра, ни других экземпляров этого класса)?
Asked
Active
Viewed 484 times
9
-
сделайте статическую переменную и меняйте при первом вызове ей значение – splash58 Apr 23 '16 at 16:39
-
@splash58 хранить ради этого целую переменную???!!! – hero Apr 23 '16 at 16:44
-
@hero это простейший вариант. – AivanF. Apr 23 '16 at 16:45
-
@hero я думаю, любой другой код займет больше, чем байт на класс – splash58 Apr 23 '16 at 16:45
-
Формально вызвать-то можно будет, другое дело, что она ничего не будет делать. Но вопрос-то сформулирован не так... – Harry Apr 23 '16 at 17:00
-
4Целую переменную? Вы наверное имели ввиду всего одну переменную? Вам жалко одного байта? – VladD Apr 23 '16 at 17:06
2 Answers
13
Для этого в C++ есть специальная функция и флаг, делается это так:
std::once_flag flag;
//...
class Class
{
Class()
{
std::call_once(flag, [this]{ SomeMethod(); });
}
void SomeMethod()
{...}
}
Таким образом мы вызовем SomeMethod() в конструкторе однажды, но это не запретит вызывать этот метод в других местах программы, поэтому его можно сделать приватным, но это не запретит вызов приватного метода в других методах класса. Для того, чтобы полностью исключить повторный вызов какого-либо кода, нужно весь этот код поместить в лямбду, которая передаётся в std::call_once
std::once_flag flag;
//...
class Class
{
Class()
{
std::call_once(flag, [this]
{
// Тут будет код, который нужно вызывать лишь единожды
});
}
}
ixSci
- 23,825
-
-
@hero, для начала покажите Ваш случай. У Вас в вопросе слишком мало данных – ixSci Apr 23 '16 at 17:42
-
Просто непонятно для чего
[], функция должна вызываться в функторе что ли? – hero Apr 23 '16 at 17:49 -
@hero, это лямбда. Необязательно её использовать, конечно, просто так проще. Всё зависит от того, какую функцию Вы вызываете – ixSci Apr 23 '16 at 17:50
-
-
-
-
-
-
2Формально никто не мешает вызвать
SomeMethodотдельно - http://ideone.com/hPeUbN Вопрос же стоит "которую в дальнейшем нельзя будет вызывать (ни с помощью данного экземпляра, ни других экземпляров этого класса)". В лучшем случае это должна быть лямбда, область видимости которой - конструктор, но никак не функция из области видимости класса. По-моему, вопрос стоит переформулировать более корректно :) – Harry Apr 23 '16 at 18:09 -
@Harry, ну тогда просто перенести вызов
call_onceв функцию, думаю, что автор разберётся. – ixSci Apr 23 '16 at 18:13 -
-
@ixSci Понимаете, лично я воспринял вопрос так, что запрет повторного вызова - на уровне компилятора, а это невозможно ну никак. Imho. Наверное, я чересчур формалист, да, но ведь и "чтоб больше нельзя было вызвать" - это полный запрет на вызов. А не "чтобы вызов вернулся, ничего не делая", например... – Harry Apr 23 '16 at 18:18
-
@Harry, а зачем такое может быть? Чем ситуации будут отличаться? Человек формулирует вопрос в рамках своего понимания темы, озвучивает желание — оно не всегда чёткое, поэтому ожидать чётких и формальных вопросов не стоит, я считаю. – ixSci Apr 23 '16 at 18:24
-
-
1@ixSci Ну, я сам иногда задаю очень теоретичные вопросы... :) И меня этот вопрос заинтересовал именно с этой точки зрения. По здравом размышлении пришел к выводу, что для функции-члена это нереально. Но хотел бы убедиться, чтоб гуру то же самое сказали :) – Harry Apr 23 '16 at 19:17
-
@ixSci, Это всё хорошо, но! разве если экземпляр класса был создан до того, как установлен флаг, вызов не произойдёт? – Isaev Apr 28 '16 at 13:54
-
@Isaev, не совсем понял, что Вы имеет в виду, но тот код, что находится в лямбде, которая передаётся в
call_once, гарантировано будет вызван лишь один раз. – ixSci Apr 28 '16 at 15:22
7
Просто заведите статическую переменную-флаг и обрабатывайте его:
foo::foo()
{
static bool once = true;
if(once)
{
once = false;
bar();
}
}
Второй вариант более элегантный, но работает только если bar возвращает какое-либо значение, то есть не void:
int bar();
foo::foo()
{
static const auto once = bar();
}
Cerbo
- 6,863
-
-
@avp, а больше не о чем дебатировать, синглтоны теперь в C++ имеют один вид и пишутся элементарно. – ixSci Apr 24 '16 at 04:27
-
-
@avp,
static Object& instance(){static Object instance; return instance;}. По вкусу, можно возвращать указатель – ixSci Apr 24 '16 at 09:22 -
@ixSci, тогда я не что-то не понимаю. Вопрос ТС это фактически про singleton. Почему же среди ответов нет Вашего варианта, а есть 2 других и оба сильно заплюсованы? – avp Apr 24 '16 at 09:26
-
@avp, вопрос можно свести к синглтону, но он не о нём. Так, Cerbo, в целом и привёл пример, сведённый к синглтону, только вместо объекта, он использовал функцию. С чистым синглтоном получилось бы больше кода(добавлять новый класс совершенно не имеет смысла, в рамках данной задачи) – ixSci Apr 24 '16 at 09:33
-
@ixSci, дело в том, что в многопоточной среде этот код в принципе не безопасен. Поэтому я и упомянул синглтон, но похоже, что тут никто так глубоко не смотрит. – avp Apr 24 '16 at 09:57
-
@avp, мой код полностью потокобезопасен. Про код Cerbo не уверен, но скорее всего тоже должен быть безопасен — нужно штудировать стандарт. – ixSci Apr 24 '16 at 10:03
-
@ixSci, я про Cerbo. Для безопасности там нужен мьютекс (где и как это написать в крестах -- другой вопрос). Сейчас, если переключение потоков (по прерыванию от таймера) произойдет после if, но до записи false (причем такой, что другие потоки это увидят)
bar()может выполниться не один раз. – avp Apr 24 '16 at 10:18 -
@avp, я говорил про второй кусок. Он, насколько я понимаю, потокобезопасен. Чтобы удостоверится, нужно найти в стандарте, что вызов
barявляется частью инициализации(в чём я почти на 100 уверен). Первый кусок, да, не является безопасным, с точки зрения многопоточной среды. – ixSci Apr 24 '16 at 10:21 -
@ixSci, второй да. Понятно, что инициализация статиков происходит однократно да вызова main. И вот тут большой вопрос, какое состояние памяти видит bar. (логически продолжая подобные рассуждения приходим к выводу, что С++ не годится для программирования сложных вещей). – avp Apr 24 '16 at 10:28
-
@avp, без обид, но это не C++ не предназначен, а просто Вы его знаете не очень хорошо. Не будет тут статик проинициализирован до main, он будет проинициализирован в момент вызова
fooи только один раз, гарантировано только одним потоком. Это гарантирует стандарт C++. – ixSci Apr 24 '16 at 11:36 -
А почему можно свести вопрос к сингтону? Вообще поведение, возможно, необходимое для регистрации каких-то уникальных ID в какой-то фабрике при первом использовании класса. Экземпляров может быть много, а вот выполнять регистрацию можно только раз. @avp, да, тов. ixSCi прав по поводу статиков. Всегда было, что статик уровня функции инициализируюется при первом вызове этой функции, а уровня единицы трансляции - до main. C++11 стал гарантировать что такие инициализации будут потокобезопасными. (почему-то опубликовалось в виде ответа) – Monah Tuk Apr 25 '16 at 08:52
-
@ixSci, ага (раз гарантируется такое поведение), тогда будет работать. Но С++ все равно не годиться, он слишком сложен, слишком много деталей. Где найти за приемлемую плату грамотных людей на поддержку? Недаром, даже в opensource популярность С и С++ одинакова, а в Tiobe C популярней вдвое. – avp Apr 25 '16 at 10:03