2013-02-22 59 views
6

All,std :: unique_ptr xóa chức năng, initializer_list - điều khiển phân bổ

Khi tôi khởi tạo một mảng các widget sử dụng định dạng initializer-list, một con trỏ thường trỏ đến một trường hợp phụ tùng thành viên biến biên dịch nhưng sau khi thay đổi std :: unique_ptr <> gcc cung cấp một lỗi biên dịch liên quan đến một hàm bị xóa.

$ uname -a

Linux .. 3.5.0-21-generiC# 32-Ubuntu SMP Tue ngày 11 tháng 12 18:51:59 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux

$ g ++ - -version

g ++ (Ubuntu/Linaro 4.7.2-5ubuntu1) 4.7.2

mã này cung cấp cho các lỗi biên dịch sau:

#include <stdlib.h> 
#include <memory> 

class Widget 
{ 
public: 
    Widget() {} 
}; 

class W1 : public Widget 
{ 
public: 
    W1() {} 
}; 

class W2 : public Widget 
{ 
public: 
    W2() {} 
}; 

class WFactory 
{ 
public: 
    WFactory(const int i) : _w(new W1()) {} 
    WFactory(const char* s) : _w(new W2()) {} 

    ~WFactory() { _w.reset(nullptr); } 
    // ~WFactory() { delete _w; } <--- for naked ptr 

private: 
    // NOTE: does not compile 
    std::unique_ptr<Widget> _w; 
    // NOTE: does compile 
    // Widget* _w; 
}; 

int main() 
{ 
    std::unique_ptr<Widget> a(new W1()); // <--- compiles fine 

    WFactory wf[] { 4, "msg" };   // <--- compiler error using unique_ptr<> 
} 

lỗi:

$ g++ -o unique_ptr -std=c++11 -Wall unique_ptr.cpp 
unique_ptr.cpp: In function ‘int main()’: 
unique_ptr.cpp:36:30: error: use of deleted function ‘WFactory::WFactory(const WFactory&)’ 
unique_ptr.cpp:22:7: note: ‘WFactory::WFactory(const WFactory&)’ is implicitly deleted because the default definition would be ill-formed: 
unique_ptr.cpp:22:7: error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = Widget; _Dp = std::default_delete<Widget>; std::unique_ptr<_Tp, _Dp> = std::unique_ptr<Widget>]’ 
In file included from /usr/include/c++/4.7/memory:86:0, 
      from unique_ptr.cpp:2: 
/usr/include/c++/4.7/bits/unique_ptr.h:262:7: error: declared here 
unique_ptr.cpp:36:30: error: use of deleted function ‘WFactory::WFactory(const WFactory&)’ 
unique_ptr.cpp:36:14: warning: unused variable ‘wf’ [-Wunused-variable] 

Tôi đang ở một mất mát như một trong hai: các cơ chế đằng sau hậu trường rằng sản lượng một fcxn xóa; hoặc đơn giản hơn, tại sao tính biểu cảm của std :: unique_ptr <> dường như bị hạn chế so với w/ptr trần truồng.

Câu hỏi của tôi là: lỗi

  • thí điểm?
  • lỗi trình biên dịch?
  • tôi có thể lấy mã dự định để làm việc với một số thay đổi không?

Cảm ơn bạn.

Sửa 1

Dựa trên câu trả lời của bạn, mà tôi đánh giá cao, tôi có thể thực hiện thay đổi sau đây để WFactory:

(gắn cờ mã như vô đạo đức)

class WFactory 
{ 
public: 
    WFactory(const WFactory& wf) 
    { 
     (const_cast<WFactory&>(wf)).moveto(_w); 
    } 

    WFactory(const int i) : _w(new W1()) {} 
    WFactory(const char* s) : _w(new W2()) {} 

    ~WFactory() { _w.reset(nullptr); } 

    void moveto(std::unique_ptr<Widget>& w) 
    { 
     w = std::move(_w); 
    } 
private: 
    std::unique_ptr<Widget> _w; 
}; 

và bây giờ chương trình biên dịch và chạy. Tôi đánh giá cao rằng các tiêu chuẩn folks đã viết các đặc điểm kỹ thuật cho một lý do, vì vậy tôi đăng kết quả của tôi như là một chuyên môn tốt đức tin cho trường hợp của tôi ở bàn tay, nơi tôi thực sự muốn nhấn mạnh sự độc đáo của ptr.

Chỉnh sửa 2

Dựa trên trả lời của Jonathan, đoạn code sau không ngăn chặn di chuyển ctor ngầm:

class WFactory 
{ 
public: 
    WFactory(const int i) : _w(new W1()) {} 
    WFactory(const char* s) : _w(new W2()) {} 

private: 
    std::unique_ptr<Widget> _w; 
}; 

Lưu ý rằng không có ~WFactory() {..} ở tất cả.

Có lẽ có ya-ans, nhưng tôi đã thấy rằng việc sử dụng lặp lại kiểu C++ 11 trên wf [] trong Main() sẽ trả về lỗi no-copy-ctor-for-WFactory.Đó là:

int Main() 
.. 
    WFactory wf[] { 4, "msg" }; 

    for (WFactory iwf : wf) <---- compiler error again 
     // .. 

    for (unsigned i = 0; i < 2; ++i) <--- gcc happy 
     wf[i] // .. 
} 

Tôi đoán đó là hiển nhiên rằng C++ 11 kiểu lặp mới được làm một bản sao đối tượng.

+0

Việc khởi tạo qua danh sách được sắp xếp yêu cầu các đối tượng được sao chép chính thức, thật không may. –

+0

Cảm ơn bạn, điều đó thật thú vị. Bản sao ở đâu? Lỗi chỉ ra rằng WFactory phải được sao chép. Tôi không hiểu. – JayInNyc

+0

Đó là thông số kỹ thuật của ngôn ngữ. Bản sao sẽ không thực sự xảy ra trong thực tế, nhưng lớp vẫn cần phải có một hàm tạo bản sao có thể truy cập. –

Trả lời

8

Theo Đoạn 8.5.1/2 của C++ 11 Tiêu chuẩn:

When an aggregate is initialized by an initializer list, as specified in 8.5.4, the elements of the initializer listare taken as initializers for the members of the aggregate, in increasing subscript or member order. Each member is copy-initialized from the corresponding initializer-clause. [...]

Đối với mỗi phần tử, sau đó, copy-khởi tạo liên quan đến việc tạo ra một tạm thời của các loại đích, mà sau đó sử dụng để sao chép-xây dựng các yếu tố của mảng.

Tuy nhiên, lớp học của bạn chứa thành viên có loại là phiên bản unique_ptr, không thể sao chép được. Điều đó làm cho lớp của bạn không thể sao chép được.

Hơn nữa, mặc dù unique_ptrdi chuyển, lớp học của bạn không phải vì thế hệ ngầm của trình dịch chuyển di chuyển của trình biên dịch bị chặn bởi sự hiện diện của một hàm hủy xác định rõ ràng. Nếu đó không phải là trường hợp (tức là, nếu bạn định nghĩa rõ ràng một hàm khởi tạo cho lớp của bạn), thì việc khởi tạo sao chép sẽ hoạt động (xem 8.5/15).

Hãy thử thay đổi định nghĩa của WFactory như sau để thấy rằng:

class WFactory 
{ 
public: 
    WFactory(const int i) : _w(new W1()) {} 
    WFactory(const char* s) : _w(new W2()) {} 
    WFactory(WFactory&& f) : _w(std::move(f._w)) {} 
    ~WFactory() { _w.reset(nullptr); } 
private: 
    std::unique_ptr<Widget> _w; 
}; 

int main() 
{ 
    std::unique_ptr<Widget> a(new W1()); 
    WFactory wf[] { 4, "msg" };   // OK 
} 
+0

Vui lòng xem chỉnh sửa ở trên. Cám ơn bạn một lần nữa. – JayInNyc

+0

[dcl.init]/15 "[_Lưu ý: _ Sao chép khởi tạo có thể gọi di chuyển (12.8). - _end note] _" –

+0

@ Jonathanathan: Rất chính xác. Hãy để tôi chỉnh sửa. Cảm ơn bạn. –

4

the mechanics behind the scenes that yields a deleted fcxn;

Mảng chỉ có thể được khởi tạo như thế nếu gõ là copyable hoặc di chuyển, và unique_ptr không copyable, do đó, một lớp với một thành viên unique_ptr không thể sao chép theo mặc định và loại của bạn có trình phá hủy do người dùng xác định, điều này ngăn cản hàm khởi động ẩn, vì vậy loại của bạn cũng không thể di chuyển được.

or more simply, why the expressiveness of std::unique_ptr<> appears to be restricted compared w/ a naked ptr.

unique_ptr đang giúp bạn tránh khỏi lỗi nghiêm trọng. Với con trỏ khỏa thân, kiểu của bạn không an toàn và sẽ dẫn đến hành vi không xác định, bởi vì bạn không có một hàm tạo bản sao để con trỏ được sao chép và sau đó bị xóa hai lần bởi hai đối tượng khác nhau. Boom, chương trình của bạn có hành vi không xác định. unique_ptr sửa lỗi lớp học của bạn bằng cách ngăn không cho nó được sao chép, an toàn và chính xác.

Bạn có thể làm cho nó hoạt động theo nhiều cách, cách dễ nhất là xóa trình phá hủy do người dùng xác định, làm cho lớp của bạn có thể di chuyển và khởi tạo mảng sẽ biên dịch. Hoặc nếu bạn cần một destructor do người dùng xác định vì một lý do nào khác, bạn vẫn có thể làm cho nó hoạt động bằng cách viết một hàm tạo di chuyển do người dùng định nghĩa để di chuyển rõ ràng thành viên _w.

Nếu vì một lý do nào đó không phải là có thể, bạn có thể làm cho nó hoạt động bằng cách thêm một constructor mặc định, vì vậy các phần tử mảng có thể mặc định được xây dựng, và sau đó di chuyển-giao cho họ:

class WFactory 
{ 
public: 
    WFactory() = default; 
    WFactory(const int i) : _w(new W1()) {} 
    WFactory(const char* s) : _w(new W2()) {} 

private: 
    std::unique_ptr<Widget> _w; 
}; 

int main() 
{ 
    WFactory wf[2]; 
    wf[0] = WFactory(4); 
    wf[1] = WFactory("msg"); 
} 

của bạn phiên bản chỉnh sửa là vô đạo đức và rất đáng ngờ, bạn không nên bỏ đi const như thế và bạn không nên di chuyển từ một giá trị, đặc biệt không phải là một giá trị const. Không đi đến đó.Thay vì thay đổi cách bạn sử dụng lớp để tránh phải sao chép nó, hay viết một constructor bản sao hợp lệ mà không một bản sao sâu của đối tượng sở hữu:

class Widget 
{ 
public: 
    Widget() {} 
    virtual std::unique_ptr<Widget> clone() const = 0; 
}; 

class W1 : public Widget 
{ 
public: 
    W1() {} 
    virtual std::unique_ptr<Widget> clone() const 
    { return std::unique_ptr<Widget>(new W1(*this)); } 
}; 

class W2 : public Widget 
{ 
public: 
    W2() {} 
    virtual std::unique_ptr<Widget> clone() const 
    { return std::unique_ptr<Widget>(new W2(*this)); } 
}; 

class WFactory 
{ 
public: 
    WFactory(const int i) : _w(new W1()) {} 
    WFactory(const char* s) : _w(new W2()) {} 
    WFactory(const WFactory& w) : _w(w._w->clone()) {} 
    // ... 

Cách tiếp cận tốt nhất là làm cho lớp di chuyển, và cách tốt nhất để thực hiện điều đó là thực hiện theo các số rule of zero

+0

Tôi thích rằng chỉnh sửa của tôi là vô đạo đức ;-) Điều đó đặt một nụ cười trên khuôn mặt của tôi (thực sự). – JayInNyc

+0

"Với con trỏ khỏa thân, kiểu của bạn không an toàn và sẽ dẫn đến hành vi không xác định, bởi vì bạn không có một trình tạo bản sao để con trỏ được sao chép và sau đó bị xóa hai lần bởi hai đối tượng khác nhau. Boom, chương trình của bạn có hành vi không xác định. sửa lớp của bạn bằng cách ngăn không cho nó được sao chép, an toàn và chính xác. " - vì vậy chúng tôi đồng ý. Đây là nguồn cảm hứng cho câu hỏi ban đầu của tôi. – JayInNyc

+0

FYI ứng dụng của tôi không thể gọi wf [0] = ..., wf [1] = ... wf [] {..} là cơ chế mà tôi (hiện tại) yêu cầu. – JayInNyc

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