// хитрый, возвращает ссылку на последний элемент
Присвоение копирует последний элемент, прежде чем он вытолкнет из вектора, так что все в порядке.
Как вы говорите, "верх" бессмысленен. Вы можете получить размер вектора в любое время, когда захотите.
Вы должны когда-либо вызывать stack.empty () только с удержанной блокировкой, так как нет гарантии, что он делает атомарный доступ. Вы можете получить противоречивый ответ, если вызовите его, когда другой поток находится в процессе обновления стека. Таким образом, ваша общедоступная функция IsEmpty должна принимать мьютекс, а это значит, что вы не хотите вызывать его самостоятельно из других источников.
Но в любом случае IsEmpty не очень полезен в параллельном коде. То, что когда вы звоните, это ложно, это не значит, что оно будет ложным через одну строку, когда вы щелкаете. Так что либо вы должны избавиться от него из открытого интерфейса, либо вы должны выставить блокировку, чтобы пользователи могли писать свои собственные атомарные операции. В этом случае у меня не было бы никакой проверки недостаточного значения, кроме assert в режиме отладки. Но тогда я никогда не верил в тех, кто бездельничает, которые заходят в режим релиза, не читая документацию и не тестируя свой код.
[Редактировать: Как использовать RAII для блокировок
Когда люди говорят использовать RAII для блокировки, они не просто хотят убедиться, что мьютекс уничтожен. Они хотят использовать его, чтобы убедиться, что мьютекс разблокирован. Дело в том, что если у вас есть код, который выглядит так:
lock();
doSomething();
unlock();
и doSomething () выдает исключение, тогда вы не разблокируете мьютекс. Уч.
Итак, вот пример класса вместе с использованием:
class LockSession;
class Lock {
friend class LockSession;
public:
Lock() { pthread_mutex_init(&lock); }
~Lock() { pthread_mutex_destroy(&lock); }
private:
void lock() { pthread_mutex_lock(&lock); }
void unlock() { pthread_mutex_unlock(&lock); }
private:
Lock(const Lock &);
const Lock &operator=(const Lock &);
private:
pthread_mutex_t lock;
};
class LockSession {
LockSession(Lock &l): lock(l) { lock.lock(); }
~LockSession() { lock.unlock(); }
private:
LockSession(const LockSession &);
LockSession &operator=(const LockSession &);
private:
Lock &lock;
};
Тогда где-то ваш код будет иметь блокировку, связанную с данными, которые вы хотите защитить, и будет использовать что-то вроде следующего:
void doSomethingWithLock() {
LockSession session(lock);
doSomething();
}
или
void doSeveralThings() {
int result = bigSlowComputation(); // no lock
{
LockSession s(lock);
result = doSomething(result); // lock is held
}
doSomethingElse(result); // no lock
}
Теперь не имеет значения, выдает ли doSomething()
исключение или возвращает нормально (ну, во втором примере doSomethingElse
не произойдет при исключении, но я предполагаю, что это не нужно сделано в ситуации ошибки). В любом случае, session
уничтожается, и его деструктор освобождает мьютекс. В частности, такие операции, как «push» в стеке, выделяют память и, следовательно, могут сбрасывать, и поэтому вам нужно с этим справиться.
RAII расшифровывается как «Приобретение ресурсов - инициализация». В случае doSomethingWithLock () ресурс, который вы хотите получить, - это то, что вы хотите удерживать блокировку. Итак, вы пишете класс, который позволяет вам делать это путем инициализации объекта (LockSession). Когда объект разрушен, замок освобождается. Таким образом, вы относитесь к «блокированию / разблокированию мьютекса» точно так же, как вы относитесь к «инициации / деинсталляции мьютекса», и вы таким же образом защищаете себя от утечки ресурсов.
Один немного раздражающий факт заключается в том, что этот код полностью сломан и содержит ошибки, и вы должны быть уверены, что случайно его не сделаете, даже если он выглядит неосторожным взглядом, как правильный код:
void doSomethingWithLock() {
LockSession(lock);
doSomething();
}
Здесь первая строка создает временный объект и немедленно уничтожает его, снова снимая блокировку. doSomething()
не вызывается при удерживаемой блокировке.
Boost имеет шаблон класса scoped_lock
, который делает то же, что и LockSession, и многое другое.]