2012-04-25 2 views
26

Я недавно начал портирование много моего существующего C++ кода приложения до более чем на C++ 11 и теперь, когда я конвертировании в новых смарт-указатели зЬй :: unique_ptr и станд: : shared_ptr, у меня есть конкретный вопрос о пользовательских удалениях. Я хочу добавить лямбда-регистратор, чтобы увидеть, где вызывается мое удаление, но я не могу получить версию специализации массива для компиляции. Совет будет очень признателен.unique_ptr <T> лямбда пользовательских Deleter для специализации массива

Я искал напрасно пример пользовательского Deleter для массива специализации unique_ptr для VC++ 10 или GCC 4.5.2+. Я хотел бы напечатать сообщение журнала, когда вызывающие вызовы вызываются в лямбда - главным образом, чтобы убедиться, что все указатели, которые, как я думаю, выходят за рамки, делают это. Возможно ли это для версии массива специализации? Я могу заставить его работать с версией без массива, и я также могу заставить его работать с специализацией массива, если передать в качестве второго аргумента внешнюю структуру «MyArrayDeleter». Еще одна вещь, можно было бы удалить уродливую std :: function, поскольку я думал, что я могу позволить этой цифре лямбда-подписи.

struct MySimpleDeleter { 
    void operator()(int* ptr) const { 
     printf("Deleting int pointer!\n"); 
     delete ptr; 
    } 
}; 
struct MyArrayDeleter { 
    void operator()(int* ptr) const { 
     printf("Deleting Array[]!\n"); 
     delete [] ptr; 
    } 
}; 
{ 
    // example 1 - calls MySimpleDeleter where delete simple pointer is called 
    std::unique_ptr<int, MySimpleDeleter> ptr1(new int(5)); 

    // example 2 - correctly calls MyArrayDeleter where delete[] is called 
    std::unique_ptr<int[], MyArrayDeleter> ptr2(new int[5]); 

    // example 3 - this works (but default_delete<int[]> would have been passed 
    // even if I did not specialize it as it is the default second arg 
    // I only show it here to highlight the problem I am trying to solve 
    std::unique_ptr<int[], std::default_delete<int[]>> ptr2(new int[100]); 

    // example 3 - this lambda is called correctly - I want to do this for arrays 
    std::unique_ptr<int, std::function<void (int *)>> ptr3(
     new int(3), [&](int *ptr){ 
      delete ptr; std::cout << "delete int* called" << std::endl; 
     }); 

    // example 4 - I cannot get the following like to compile 
    // PLEASE HELP HERE - I cannot get this to compile 
    std::unique_ptr<int[], std::function<void (int *)>> ptr4(
     new int[4], [&](int *ptr){ 
      delete []ptr; std::cout << "delete [] called" << std::endl; 
     }); 
} 

The compiler error is as follows: 

The error from the compiler (which complains about the new int[4] for ptr4 below is: 
'std::unique_ptr<_Ty,_Dx>::unique_ptr' : cannot access private member declared in class 'std::unique_ptr<_Ty,_Dx>' 
1>   with 
1>   [ 
1>    _Ty=int [], 
1>    _Dx=std::tr1::function<void (int *)> 
1>   ] 
1>   c:\program files (x86)\microsoft visual studio 10.0\vc\include\memory(2513) : see declaration of 'std::unique_ptr<_Ty,_Dx>::unique_ptr' 
1>   with 
1>   [ 
1>    _Ty=int [], 
1>    _Dx=std::tr1::function<void (int *)> 
1>   ] 
+0

пример 3 сохранен я .. спасибо –

ответ

32

насчет:

auto deleter=[&](int* ptr){...}; 
std::unique_ptr<int[], decltype(deleter)> ptr4(new int[4], deleter); 
+2

Тип аргумента вашего неверного - - это должно быть 'int * ptr', а не' int (* ptr) [] '. – ildjarn

+0

@ildjarn: почему' int (* ptr) [] 'неправильный? Должен ли я ожидать' std :: unique_ptr ' передать 'deleter' указатель на массив' int 'вместо указателя на один int? Или это написано' int * (ptr []) '? – Managu

+4

В C++ динамический массив' int ' представляется в виде 'int *', т. е. тип 'new int [4]' is 'int *'. Следовательно, делетер ожидает 'int *' - почему бы дополнительный уровень косвенности был полезным/необходимым Это легко продемонстрировать, фактически пытаясь выполнить _compile_ ваш код ..: [не работает] (http://ideone.com/fujAk), [работает] (http://ideone.com/JeXYD). нет контекста 'int (* ptr) []' действительный синтаксис; по крайней мере, это должно быть 'int (* ptr) [N]' для некоторого известного 'N', чтобы действовать как указатель на массив _statically_. – ildjarn

4

Прежде первый, я использую VC2010 с пакетом обновлением 1, Mingw г ++ 4.7.1

Для массива нового, unique_ptr уже поддерживает его в чистом виде:

struct X 
{ 
    X() { puts("ctor"); } 
    ~X() { puts("dtor"); } 
}; 

unique_ptr<X[]> xp(new X[3]); 

выход:

ctor 
ctor 
ctor 
dtor 
dtor 
dtor 

Для индивидуальных Deleter, к сожалению, это непоследовательно между VC2010 и г ++:

VC2010:

unique_ptr<FILE, function<void (FILE*)> > fp(fopen("tmp.txt", "w"), [](FILE *fp){ 
    puts("close file now"); 
    fclose(fp); 
    }); 

г ++:

unique_ptr<FILE, void (*)(FILE*) > fp(fopen("tmp.txt", "w"), [](FILE *fp){ 
    puts("close file now"); 
    fclose(fp); 
    }); 

Способ по Managu очень хорошо, потому что инлайн лямбда это круто, но больно читаемость ИМХО. Он также подчеркивает, что релиз ресурсов до приобретения (RAII).

Здесь я предлагаю declartive способ отделить приобретение ресурсов и выпуска (Scope Guard, работает как для VC2010 и г ++ 4.7.1):

template<typename T> 
struct ScopeGuard 
{ 
    T deleter_; 
    ScopeGuard(T deleter) : deleter_(deleter) {} 
    ~ScopeGuard() { deleter_() ; } 
}; 
#define UNI_NAME(name, line) name ## line 
#define ON_OUT_OF_SCOPE_2(lambda_body, line) auto UNI_NAME(deleter_lambda_, line) = [&]() { lambda_body; } ; \ 
     ScopeGuard<decltype(UNI_NAME(deleter_lambda_, line))> \ 
     UNI_NAME(scope_guard_, line) (UNI_NAME(deleter_lambda_, line)); 
#define ON_OUT_OF_SCOPE(lambda_body) ON_OUT_OF_SCOPE_2(lambda_body, __LINE__) 

FILE * fp = fopen("tmp.txt", "w"); 
ON_OUT_OF_SCOPE({ puts("close file now"); fclose(fp); }); 

Дело в том, вы можете получить ресурс в старом, ясный способ и объявить оператор для выпуска ресурса сразу же после линии сбора ресурсов.

Недостатком является то, что вы не можете переместить один объект вместе с его делетером.

Для FILE *, shared_ptr может быть использован в качестве альтернативного указателя для тех же целей (может быть немного тяжеловесных, но хорошо работает как для VC2010 и г ++)

shared_ptr FP2 (FOPEN ("TMP.txt "," w "), [] (FILE * fp) {fclose (fp); puts (" закрыть файл ");});

 Смежные вопросы

  • Нет связанных вопросов^_^