2012-04-25 25 views
26

Tôi vừa mới bắt đầu porting nhiều C++ hiện tại của tôi mã ứng dụng để giao cho C++ 11 và bây giờ mà tôi đang chuyển sang các con trỏ thông minh mới std :: unique_ptrstd: : shared_ptr, tôi có câu hỏi cụ thể về các thông số tùy chỉnh. Tôi muốn thêm một logger lambda để xem nơi xóa của tôi đang được gọi nhưng tôi không thể có được phiên bản chuyên môn mảng để biên dịch. Lời khuyên sẽ được đánh giá rất nhiều.unique_ptr <T> lambda tùy chỉnh deleter cho chuyên môn mảng

Tôi đã được tìm kiếm trong vô vọng cho một ví dụ về một deleter tùy chỉnh cho mảng chuyên môn unique_ptr cho VC++ 10 hoặc GCC 4.5.2+. Tôi muốn in một thông điệp tường trình khi các deleters được gọi trong một lambda - chủ yếu để đảm bảo rằng tất cả các con trỏ mà tôi nghĩ là đi ra khỏi phạm vi đang làm như vậy. Điều này có thể cho phiên bản mảng chuyên môn không? Tôi có thể làm cho nó làm việc với phiên bản không mảng, và tôi cũng có thể làm cho nó hoạt động với một chuyên môn mảng nếu tôi chuyển một cấu trúc bên ngoài "MyArrayDeleter" làm đối số thứ hai. Một điều nữa, nó sẽ có thể loại bỏ các xấu xí std :: chức năng như tôi nghĩ rằng tôi có thể để cho con số chữ ký lambda mà ra.

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

ví dụ 3 đã lưu tôi .. cảm ơn –

Trả lời

32

gì về:

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

Loại đối số của bạn không đúng - - đó phải là 'int * ptr' thay vì' int (* ptr) [] '. – ildjarn

+0

@ildjarn: tại sao' int (* ptr) [] 'sai? Tôi không nên mong đợi' std :: unique_ptr ' để vượt qua 'deleter' một con trỏ tới một mảng' int 'thay vì một con trỏ tới một int đơn lẻ? Hoặc được đánh vần' int * (ptr []) '? – Managu

+4

Trong C++, một mảng động của' int ' được biểu diễn như là một 'int *', tức là, kiểu 'int mới [4]' là 'int *'. Do đó, deleter mong đợi một 'int *' - tại sao một mức thụt lề bổ sung lại hữu ích/cần thiết Điều này dễ dàng được chứng minh bằng cách thực sự cố gắng _compile_ mã của bạn ..: [không hoạt động] (http://ideone.com/fujAk), [không hoạt động] (http://ideone.com/JeXYD). không có ngữ cảnh nào là 'int (* ptr) []' cú pháp hợp lệ; ít nhất nó sẽ phải là 'int (* ptr) [N]' đối với một số 'N' đã biết, hoạt động như một con trỏ tới một mảng có kích thước _statically_. – ildjarn

4

Trước tiên, tôi sử dụng VC2010 với SP1, MinGW g ++ 4.7.1

Đối với mảng mới, unique_ptr đã hỗ trợ nó trong một cách sạch sẽ:

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

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

Đầu ra là:

ctor 
ctor 
ctor 
dtor 
dtor 
dtor 

Đối deleter tùy chỉnh, không may, đó là mâu thuẫn giữa VC2010 và g ++:

VC2010:

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

g ++:

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

Phương pháp bởi Managu là rất tốt, vì inline lambda là mát mẻ nhưng đau IMHO dễ đọc. Nó cũng nhấn mạnh rằng tài nguyên phát hành trước khi mua lại (RAII).

Ở đây tôi đề nghị một cách declartive để tách mua lại tài nguyên và phát hành (Phạm vi Guard, làm việc cho cả hai VC2010 và g ++ 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); }); 

Vấn đề là bạn có thể nhận được một nguồn tài nguyên ở người già, cách rõ ràng và khai báo câu lệnh để giải phóng tài nguyên ngay sau dòng chuyển đổi tài nguyên.

Hạn chế là bạn không thể chuyển tiếp một đối tượng xung quanh cùng với dấu phân cách của nó.

Đối FILE *, shared_ptr có thể được sử dụng như một con trỏ thay thế cho các mục đích tương tự (có thể là một chút nặng cân, nhưng hoạt động tốt cho cả hai VC2010 và g ++)

shared_ptr fp2 (fopen ("tmp.txt "," w "), [] (FILE * fp) {fclose (fp); đặt (" đóng tệp ");});

Các vấn đề liên quan