2013-04-23 38 views
5

Trong khi triển khai lớp nhà máy, tôi gặp phải hành vi của std::auto_ptr mà tôi không thể hiểu được. Tôi giảm vấn đề xuống chương trình nhỏ sau đây, vì vậy ... chúng ta hãy bắt đầu.Mẫu Singleton: hành vi khác nhau của auto_ptr và unique_ptr

Hãy xem xét các lớp singleton sau:

singleton.h

#ifndef SINGLETON_H_ 
#define SINGLETON_H_ 

#include<iostream> 
#include<memory> 

class singleton { 
public: 
    static singleton* get() { 
    std::cout << "singleton::get()" << std::endl; 
    if (!ptr_.get()) { 
     std::cout << &ptr_ << std::endl; 
     ptr_.reset(new singleton ); 
     std::cout << "CREATED" << std::endl; 
    } 
    return ptr_.get(); 
    } 

    ~singleton(){ 
    std::cout << "DELETED" << std::endl; 
    } 
private: 
    singleton() {} 
    singleton(const singleton&){} 

    static std::auto_ptr<singleton> ptr_; 
    //static std::unique_ptr<singleton> ptr_; 
}; 

#endif 

singleton.cpp

#include<singleton.h>o 
std::auto_ptr<singleton> singleton::ptr_(0); 
//std::unique_ptr<singleton> singleton::ptr_; 

đây việc sử dụng một con trỏ thông minh để quản lý các nguồn lực chủ yếu quyết định bởi cần tránh rò rỉ khi thoát chương trình. Tôi sử dụng sau đó mã này trong các chương trình sau đây:

a.h

#ifndef A_H_ 
#define A_H_ 

int foo(); 

#endif 

a.cpp

#include<singleton.h> 

namespace { 
    singleton * dummy(singleton::get()); 
} 

int foo() { 
    singleton * pt = singleton::get(); 
    return 0; 
} 

main.cpp

#include<a.h> 

int main() { 

    int a = foo(); 

    return 0; 
} 

Bây giờ vui phần. Tôi biên dịch ba nguồn riêng biệt:

$ g++ -I./ singleton.cpp -c 
$ g++ -I./ a.cpp -c 
$ g++ -I./ main.cpp -c 

Nếu tôi liên kết chúng một cách rõ ràng theo thứ tự này:

$ g++ main.o singleton.o a.o 

tất cả mọi thứ làm việc như tôi mong đợi, và tôi nhận được những điều sau đây để stdout:

singleton::get() 
0x804a0d4 
CREATED 
singleton::get() 
DELETED 

Nếu thay vào đó, tôi liên kết các nguồn bằng cách sử dụng thứ tự này:

$ g++ a.o main.o singleton.o 

tôi nhận được kết quả này:

singleton::get() 
0x804a0dc 
CREATED 
singleton::get() 
0x804a0dc 
CREATED 
DELETED 

tôi đã cố gắng thương hiệu khác nhau biên dịch (Intel và GNU) và các phiên bản và hành vi này là phù hợp trong số đó. Nhưng dù sao, tôi không thể thấy mã có hành vi phụ thuộc vào thứ tự liên kết.

Hơn nữa, nếu auto_ptr được thay thế bằng unique_ptr hành vi luôn LUÔN phù hợp với những gì tôi mong đợi là đúng.

Điều đó đưa tôi đến câu hỏi: Có ai có đầu mối về những gì đang xảy ra ở đây không?

+0

phiên bản g ++ của bạn là gì? –

+2

Bạn có thể muốn đọc [câu hỏi này] (http://stackoverflow.com/questions/86582/). – fredoverflow

Trả lời

4

Thứ tự tại đó dummystd::auto_ptr<singleton> singleton::ptr_(0) được tạo không được chỉ định.

Đối với trường hợp auto_ptr, nếu bạn xây dựng dummy sau đó singleton::ptr_(0), giá trị được tạo ra trong dummy gọi được xoá hoàn toàn bởi các nhà xây dựng của ptr_(0).

Tôi sẽ thêm theo dõi để xây dựng ptr_ qua ptr_(([](){ std::cout << "made ptr_\n"; }(),0)); hoặc một cái gì đó tương tự.

Thực tế là nó hoạt động với unique_ptr là trùng hợp ngẫu nhiên, và có thể là do tối ưu hóa theo đó unique_ptr(0) thể tìm ra nó được zeroed, như vậy không có gì (static dữ liệu được zeroed trước khi xây dựng bắt đầu, vì vậy nếu trình biên dịch có thể hình dung ra rằng unique_ptr(0) chỉ cần 0 bộ nhớ, nó có thể bỏ qua một cách hợp pháp hàm khởi tạo, điều đó có nghĩa là bạn không còn là bộ nhớ nữa).

Một cách để khắc phục điều này là sử dụng một phương pháp mà đảm bảo xây dựng trước khi sử dụng, chẳng hạn như:

static std::auto_ptr<singleton>& get_ptr() { 
    static std::auto_ptr<singleton> ptr_(0); 
    return ptr_; 
    } 

và thay thế tài liệu tham khảo để ptr_ với get_ptr().

3

Thứ tự xây dựng đối tượng phạm vi tệp được xác định trong các đơn vị dịch khác nhau không được chỉ định. Tuy nhiên, thông thường, các đối tượng được xác định trong đơn vị dịch được liên kết trước một đơn vị dịch khác được tạo trước các đối tượng được xác định trong đơn vị dịch thứ hai. Sự khác biệt ở đây là thứ tự trong đó a.osingleton.o được liên kết. Khi singleton.o được liên kết trước a.o, singleton::ptr_ được khởi tạo trước dummy và tất cả đều tốt.Khi a.o được liên kết trước tiên, trước tiên, dummy được khởi tạo, cấu trúc đơn lẻ; sau đó singleton::ptr_ được khởi tạo thành 0, vứt con trỏ đến bản gốc của singleton. Sau đó, trong cuộc gọi đến foo, cuộc gọi đến singleton::get() sẽ tạo lại singleton.

+0

Cảm ơn rất nhiều vì đã giải thích. Tôi rất tiếc, tôi không thể chấp nhận hai câu trả lời ... – Massimiliano

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