RAII и интеллектуальные указатели в C++

У меня была та же проблема, что и для WebMvcConfigurerAdapter. Когда я искал примеры, я почти не нашел реализованного кода. Вот фрагмент рабочего кода.

создайте класс, который расширяет HandlerInterceptorAdapter

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import me.rajnarayanan.datatest.DataTestApplication;
@Component
public class EmployeeInterceptor extends HandlerInterceptorAdapter {
    private static final Logger logger = LoggerFactory.getLogger(DataTestApplication.class);
    @Override
    public boolean preHandle(HttpServletRequest request, 
            HttpServletResponse response, Object handler) throws Exception {

            String x = request.getMethod();
            logger.info(x + "intercepted");
        return true;
    }

}

, а затем реализует интерфейс WebMvcConfigurer

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import me.rajnarayanan.datatest.interceptor.EmployeeInterceptor;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Autowired
    EmployeeInterceptor employeeInterceptor ;

    @Override
    public void addInterceptors(InterceptorRegistry registry){
        registry.addInterceptor(employeeInterceptor).addPathPatterns("/employee");
    }
}
189
задан Rob Kam 27 December 2008 в 16:13
поделиться

6 ответов

Простым (и возможно злоупотребил) пример RAII является класс Файла. Без RAII мог бы выглядеть примерно так код:

File file("/path/to/file");
// Do stuff with file
file.close();

, Другими словами, мы должны удостовериться, что закрываем файл, как только мы закончили с ним. Это имеет два недостатка - во-первых, везде, где мы используем Файл, мы будем иметь на названный File::close () - если мы забудем делать это, мы держим на файл дольше, чем мы должны. Вторая проблема что, если исключение выдается, прежде чем мы закроем файл?

Java решает вторую проблему с помощью наконец пункт:

try {
    File file = new File("/path/to/file");
    // Do stuff with file
} finally {
    file.close();
}

или начиная с Java 7, оператора попытки с ресурсом:

try (File file = new File("/path/to/file")) {
   // Do stuff with file
}

C++ решает обе проблемы с помощью RAII - то есть, закрывая файл в деструкторе Файла. Пока объект Файла уничтожается в нужное время (которым это должно быть так или иначе), закрытие файла заботится о для нас. Так, наш код теперь смотрит что-то как:

File file("/path/to/file");
// Do stuff with file
// No need to close it - destructor will do that for us

Это не может быть сделано в Java, так как нет никакой гарантии, когда объект будет уничтожен, таким образом, мы не сможем гарантировать, когда ресурс, такой как файл будет освобожден.

На интеллектуальные указатели - много времени, мы просто создаем объекты на стеке. Например (и кража примера из другого ответа):

void foo() {
    std::string str;
    // Do cool things to or using str
}

Это хорошо работает - но что, если мы хотим возвратить str? Мы могли записать это:

std::string foo() {
    std::string str;
    // Do cool things to or using str
    return str;
}

Так, что случилось с этим? Ну, тип возврата является станд.:: строка - таким образом, это означает, что мы возвращаемся значением. Это означает, что мы копируем str и на самом деле возвращаем копию. Это может быть дорого, и мы могли бы хотеть избежать стоимости копирования его. Поэтому мы могли бы придумать идею возвратиться ссылкой или указателем.

std::string* foo() {
    std::string str;
    // Do cool things to or using str
    return &str;
}

, К сожалению, этот код не работает. Мы возвращаем указатель на str - но str был создан на стеке, таким образом, мы быть удаленным, как только мы выходим из нечто (). Другими словами, к тому времени, когда вызывающая сторона получает указатель, это бесполезно (и возможно хуже, чем бесполезный начиная с использования, это могло вызвать все виды броских ошибок)

Так, каково решение? Мы могли создать str на "куче", использующей новый - тот путь, когда нечто () будет завершено, str не будет уничтожен.

std::string* foo() {
    std::string* str = new std::string();
    // Do cool things to or using str
    return str;
}

, Конечно, это решение не прекрасно также. Причина состоит в том, что мы создали str, но мы никогда не удаляем его. Это не могло бы быть проблемой в очень небольшой программе, но в целом, мы хотим удостовериться, что мы удаляем его. Мы могли просто сказать, что вызывающая сторона должна удалить объект, как только он закончен с ним. Оборотная сторона - то, что вызывающая сторона должна управлять памятью, которая добавляет дополнительную сложность и могла бы понять ее превратно, ведя к утечке памяти т.е. не удалив объект даже при том, что она больше не требуется.

Это - то, где интеллектуальные указатели входят. Следующий пример использует shared_ptr - я предлагаю, чтобы Вы посмотрели на различные типы интеллектуальных указателей для изучения то, что Вы на самом деле хотите использовать.

shared_ptr<std::string> foo() {
    shared_ptr<std::string> str = new std::string();
    // Do cool things to or using str
    return str;
}

Теперь, shared_ptr будет считать количество ссылок на str. Например

shared_ptr<std::string> str = foo();
shared_ptr<std::string> str2 = str;

Теперь существует две ссылки на ту же строку. Однажды нет никаких остающихся ссылок на str, он будет удален. По сути, Вы больше не должны волноваться об удалении его сами.

Быстрое редактирование: как некоторые комментарии указали, этот пример не идеально подходит для (по крайней мере!) две причины. Во-первых, из-за реализации строк, копируя строку имеет тенденцию быть недорогим. Во-вторых, из-за то, что известно как именованная оптимизация возвращаемого значения, возвращающаяся значением, не может быть дорогим, так как компилятор может сделать некоторый ум для ускорения вещей.

Так, давайте попробуем различный пример с помощью нашего класса Файла.

Скажем, мы хотим использовать файл в качестве журнала. Это означает, что мы хотим открыться, наш файл в добавляют только режим:

File file("/path/to/file", File::append);
// The exact semantics of this aren't really important,
// just that we've got a file to be used as a log

Теперь, давайте установим наш файл как журнал для нескольких других объектов:

void setLog(const Foo & foo, const Bar & bar) {
    File file("/path/to/file", File::append);
    foo.setLogFile(file);
    bar.setLogFile(file);
}

, К сожалению, этот пример заканчивается ужасно - файл будет закрыт, как только этот метод заканчивается, означая, что нечто и панель теперь имеют недопустимый файл журнала. Мы могли создать файл на "куче" и передать указатель на файл и к нечто и к панели:

void setLog(const Foo & foo, const Bar & bar) {
    File* file = new File("/path/to/file", File::append);
    foo.setLogFile(file);
    bar.setLogFile(file);
}

, Но тогда кто ответственен за удаление файла? Если ни один не удаляет файл, то у нас есть и утечка памяти и утечка ресурсов. Мы не знаем или нечто, или панель закончится с файлом сначала, таким образом, мы не сможем ожидать, что любой удалит файл самостоятельно. Например, если нечто удаляет файл, прежде чем панель закончилась с ним, панель теперь имеет недопустимый указатель.

Так, как Вы, возможно, предположили, мы могли использовать интеллектуальные указатели для выручения нас.

void setLog(const Foo & foo, const Bar & bar) {
    shared_ptr<File> file = new File("/path/to/file", File::append);
    foo.setLogFile(file);
    bar.setLogFile(file);
}

Теперь, никто не должен волноваться об удалении файла - как только и нечто и панель закончили и больше не имеют ссылок на файл (вероятно, из-за нечто и уничтожаемой панели), файл будет автоматически удален.

311
ответ дан Michael Williamson 4 November 2019 в 15:16
поделиться

Повышение имеет много они включая тех в Повышение. Межпроцессный для общей памяти. Это значительно упрощает управление памятью, особенно в вызывающих головную боль ситуациях как то, когда у Вас есть 5 процессов, совместно использующих ту же структуру данных: когда все сделали с блоком памяти, Вы хотите, чтобы она автоматически была освобождена & не должны находиться, там пытаясь выяснить, кто должен быть ответственен за вызов delete на блоке памяти, чтобы Вы не заканчиваете с утечкой памяти или указателем, который по ошибке освобожден дважды и может повредить целую "кучу".

2
ответ дан Jason S 4 November 2019 в 15:16
поделиться

Предпосылка и причины просты в понятии.

RAII является парадигмой дизайна, чтобы гарантировать, чтобы переменные обработали всю необходимую инициализацию в своих конструкторах и всю необходимую очистку в их деструкторах. Это уменьшает всю инициализацию и очистку к одноэтапному.

C++ не требует RAII, но все больше признается, что использование методы RAII произведет больше устойчивого кода.

причина, что RAII полезен в C++, состоит в том, что C++ внутренне справляется с созданием и разрушением переменных, как они вводят и оставляют объем, текут ли через нормальный код или посредством раскручивания стека, инициированного исключением. Это - бесплатный набор в C++.

Путем связи всей инициализации и очистки к этим механизмам, Вы обеспечены, тот C++ будет заботиться об этой работе для Вас также.

Разговор о RAII в C++ обычно приводит к обсуждению интеллектуальных указателей, потому что указатели особенно хрупки когда дело доходит до очистки. Когда управление памятью типа "куча" получило от malloc или новый, это обычно - обязанность программиста освободить или удалить ту память, прежде чем указатель будет уничтожен. Интеллектуальные указатели будут использовать философию RAII, чтобы гарантировать, что выделенные объекты "кучи" уничтожаются любое время, переменная указателя уничтожается.

32
ответ дан Drew Dormann 4 November 2019 в 15:16
поделиться

Интеллектуальный указатель является изменением RAII. RAII означает, что приобретение ресурса является инициализацией. Интеллектуальный указатель получает ресурс (память) перед использованием и затем выбрасывает его автоматически в деструкторе. Две вещи происходят:

  1. Мы выделяем память , прежде чем мы будем использовать ее, всегда, даже когда мы не чувствуем себя подобно ему - трудно сделать иначе с интеллектуальным указателем. Если этого не происходило, Вы попытаетесь получить доступ к ПУСТОЙ памяти, приводящей к (очень болезненному) катастрофическому отказу.
  2. Мы освобождаем память , даже когда существует ошибка. Никакую память не оставляют, зависая.

, Например, другим примером является сетевой сокет RAII. В этом случае:

  1. Мы открываемся сетевой сокет , прежде чем мы будем использовать его, всегда, даже когда мы не чувствуем себя подобно - трудно сделать это иначе с RAII. При попытке делать это без RAII, Вы могли бы открыть пустой сокет для, сказать соединение MSN. Тогда сообщение как "позволяет, делают оно сегодня вечером" не могло бы быть передано, пользователи не займутся сексом, и Вы могли бы рискнуть увольняться.
  2. Мы закрываемся сетевой сокет , даже когда существует ошибка. Никакой сокет не оставляют, зависая, поскольку это могло бы предотвратить ответное сообщение, "уверенное плохой быть в нижней части" от того, чтобы наносить ответный удар отправителю.

Теперь, как Вы видите, RAII является очень полезным инструментом в большинстве случаев, поскольку он помогает людям заняться сексом.

источники C++ интеллектуальных указателей находятся в миллионах по всей сети включая ответы выше меня.

8
ответ дан mannicken 4 November 2019 в 15:16
поделиться

RAII Это - странное название простого, но потрясающего понятия. Лучше имя Объем Связанное управление ресурсами (SBRM). Идея состоит в том, что часто Вы, оказывается, выделяете ресурсы при начинании блока и должны выпустить его в выходе блока. Выход из блока может произойти нормальным управлением потоком, выпрыгнув из него, и даже исключением. Для покрытия всех этих случаев код становится более сложным и избыточным.

Просто пример, делающий его без SBRM:

void o_really() {
     resource * r = allocate_resource();
     try {
         // something, which could throw. ...
     } catch(...) {
         deallocate_resource(r);
         throw;
     }
     if(...) { return; } // oops, forgot to deallocate
     deallocate_resource(r);
}

, Как Вы видите, существует много способов, которыми мы можем получить pwned. Идея состоит в том, что мы инкапсулируем управление ресурсами в класс. Инициализация его объекта получает ресурс ("Приобретение Ресурса, Инициализация"). В то время, когда мы выходим из блока (область действия блока), ресурс освобожден снова.

struct resource_holder {
    resource_holder() {
        r = allocate_resource();
    }
    ~resource_holder() {
        deallocate_resource(r);
    }
    resource * r;
};

void o_really() {
     resource_holder r;
     // something, which could throw. ...
     if(...) { return; }
}

, Который хорош, если у Вас есть собственные классы, которые не являются только в целях выделения/освобождения ресурсов. Выделение просто было бы дополнительным беспокойством, чтобы сделать их задание. Но как только Вы просто хотите выделить/освободить ресурсы, вышеупомянутое становится неловким. Необходимо записать переносящийся класс для каждого вида ресурса, который Вы получаете. Для упрощения этого интеллектуальные указатели позволяют Вам автоматизировать тот процесс:

shared_ptr<Entry> create_entry(Parameters p) {
    shared_ptr<Entry> e(Entry::createEntry(p), &Entry::freeEntry);
    return e;
}

Обычно, интеллектуальные указатели являются тонкими обертками вокруг нового / удаляют, это просто, оказывается, звонит delete, когда ресурс, которым они владеют, выходит из объема. Некоторые интеллектуальные указатели, как shared_ptr позволяют Вам говорить им так называемое средство удаления, которое используется вместо delete. Это позволяет Вам, например, управлять дескрипторами окна, ресурсами регулярного выражения и другим произвольным материалом, пока Вы говорите shared_ptr о правильном средстве удаления.

существуют различные интеллектуальные указатели в различных целях:

unique_ptr

является интеллектуальным указателем, который владеет объектом исключительно. Это не находится в повышении, но это, вероятно, появится в следующем Стандарте C++. Это non-copyable, но поддерживает передача права собственности . Некоторый пример кода (следующий C++):

Код:

unique_ptr<plot_src> p(new plot_src); // now, p owns
unique_ptr<plot_src> u(move(p)); // now, u owns, p owns nothing.
unique_ptr<plot_src> v(u); // error, trying to copy u

vector<unique_ptr<plot_src>> pv; 
pv.emplace_back(new plot_src); 
pv.emplace_back(new plot_src);

В отличие от auto_ptr, unique_ptr может быть помещен в контейнер, потому что контейнеры будут в состоянии содержать non-copyable (но подвижный) типы, как потоки и unique_ptr также.

scoped_ptr

является интеллектуальным указателем повышения, который не copyable и не подвижен. Это - идеальная вещь, которая будет использоваться, когда Вы хотите удостовериться, что указатели удалены при выходе из объема.

Код:

void do_something() {
    scoped_ptr<pipe> sp(new pipe);
    // do something here...
} // when going out of scope, sp will delete the pointer automatically. 

shared_ptr

для общего владения. Для этого это и copyable и подвижно. Несколько экземпляров интеллектуального указателя могут владеть тем же ресурсом. Как только последний интеллектуальный указатель, владеющий ресурсом, выходит из объема, ресурс будет освобожден. Некоторый пример реального мира одного из моих проектов:

Код:

shared_ptr<plot_src> p(new plot_src(&fx));
plot1->add(p)->setColor("#00FF00");
plot2->add(p)->setColor("#FF0000");
// if p now goes out of scope, the src won't be freed, as both plot1 and 
// plot2 both still have references. 

, Как Вы видите, источник графика (функция fx) совместно используется, но у каждого есть отдельная запись, на которой мы выбираем цвет. Существует weak_ptr класс, который используется, когда код должен относиться к ресурсу, принадлежавшему интеллектуальному указателю, но не должен владеть ресурсом. Вместо того, чтобы передать необработанный указатель, необходимо тогда создать weak_ptr. Это выдаст исключение, когда это заметит, что Вы пытаетесь получить доступ к ресурсу weak_ptr путем доступа, даже при том, что нет никакого shared_ptr, больше владеющего ресурсом.

141
ответ дан Johannes Schaub - litb 4 November 2019 в 15:16
поделиться
void foo()
{
   std::string bar;
   //
   // more code here
   //
}

, Неважно, что происходит, панель будет правильно удаленной однажды объем нечто (), функция была оставлена позади.

Внутренне станд.:: строковые реализации часто используют ссылку считаемые указатели. Таким образом, внутренняя строка только должна быть скопирована когда одна из копий поменявших струн. Поэтому ссылка рассчитала, интеллектуальный указатель позволяет только скопировать что-то при необходимости.

, Кроме того, подсчет внутренней ссылки позволяет, что память будет правильно удалена, когда копия внутренней строки больше не будет необходима.

0
ответ дан 4 November 2019 в 15:16
поделиться
Другие вопросы по тегам:

Похожие вопросы: