2008-11-18 38 views
9

Như tiêu đề nói. Làm thế nào tôi có thể tạo một thể hiện của một lớp có sẵn trên toàn cầu (ví dụ tôi có một hàm functor cho việc in ấn và tôi muốn có một cá thể chung duy nhất của điều này (mặc dù khả năng tạo thêm)).Ví dụ toàn cầu của một lớp trong C++

+0

câu hỏi trước Đây là một ví dụ của một singleton đây trên StackOverflow: http://stackoverflow.com/questions/270947/can-any-one-provide-me-a-sample-of-singleton-in-c –

Trả lời

17

Cố gắng hết sức để tạo một đối tượng đơn lẻ bằng cách sử dụng mẫu thông thường không giải quyết được phần thứ hai của câu hỏi - khả năng tạo ra nhiều hơn nếu cần. Singleton "pattern" là rất hạn chế và không phải là bất cứ điều gì nhiều hơn một biến toàn cầu bằng tên khác.

// myclass.h 

class MyClass { 
public: 
    MyClass(); 
    void foo(); 
    // ... 
}; 

extern MyClass g_MyClassInstance; 

// myclass.cpp 

MyClass g_MyClassInstance; 

MyClass::MyClass() 
{ 
    // ... 
} 

Bây giờ, trong bất kỳ mô-đun khác chỉ bao gồm myclass.h và sử dụng g_MyClassInstance như bình thường. Nếu bạn cần phải làm nhiều hơn, có một nhà xây dựng sẵn sàng cho bạn gọi.

+0

Ugh. Điều này sẽ phá vỡ nếu MyClass phụ thuộc vào một số cá thể toàn cục khác được khởi tạo trước tiên, vì thứ tự khởi tạo của các hình cầu là "ngẫu nhiên". Tốt hơn là sử dụng lớp nhà máy thường xuyên + (tạo một cá thể). – richq

+0

@rq: Không phải ngẫu nhiên: xem http://stackoverflow.com/questions/294270/how-do-you-call-a-constructor-for-global-objects-for-arrays-of-objects-and-for # 294308 –

+0

Ngẫu nhiên, không xác định, không thể được xác định tại thời gian biên dịch ... cùng một sự khác biệt. – richq

1

Là một sửa đổi nhỏ đối với mẫu đơn, nếu bạn muốn cũng cho phép khả năng tạo nhiều phiên bản với các kiếp khác nhau, chỉ cần đặt ctors, dtor và operator = public. Bằng cách đó bạn sẽ có được cá thể toàn cục đơn lẻ thông qua GetInstance, nhưng bạn cũng có thể khai báo các biến khác trên heap hoặc stack cùng loại.

Ý tưởng cơ bản là mẫu đơn, tuy nhiên.

1

Việc thực hiện đơn giản nhất và đồng thời an toàn là Scott Meyer singleton:


#include <iostream> 

class MySingleton { 
public: 
    static MySingleton& Instance() { 
     static MySingleton singleton; 
     return singleton; 
    } 
    void HelloWorld() { std::cout << "Hello World!\n"; } 
}; 

int main() { 
    MySingleton::Instance().HelloWorld(); 
} 

Xem chủ đề IV here cho một phân tích từ John Vlissides (từ GOF nổi tiếng).

+0

nó KHÔNG đồng thời an toàn. Trong tiêu chuẩn C++, tĩnh trở thành "khởi tạo" khi hàm tạo kết thúc, vì vậy mỗi luồng sẽ nhập phương thức Instance() tại thời điểm khởi tạo sẽ gây ra singleton được tạo lại, –

+0

Dmitry Khalatov @ Về mặt kỹ thuật chính xác. Có một số giải pháp đơn giản. a) Sử dụng gcc nó có một sửa chữa rõ ràng để đối phó với điều này (không chuẩn). b) Tạo đối tượng trước bất kỳ chủ đề nào c) Sử dụng khóa bên trong phương thức Instance(). –

+0

Tôi đồng ý với @Dmitry: cho đến khi tiêu chuẩn cung cấp một cách để đối phó với sự hiện diện của chủ đề, nó không an toàn. – xtofl

3

Trước hết thực tế là bạn muốn biến toàn cầu là một 'mã mùi' (như mỗi Martin Fowler).

Nhưng để đạt được ảnh hưởng bạn muốn, bạn có thể sử dụng biến thể của Singleton.
Sử dụng các biến chức năng tĩnh. Điều này có nghĩa là biến không được tạo ra cho đến khi được sử dụng (điều này cho phép bạn đánh giá lười biếng) và tất cả các biến sẽ bị phá hủy theo thứ tự ngược lại của việc tạo (vì vậy điều này đảm bảo destructor sẽ được sử dụng).

class MyVar 
{ 
    public: 
     static MyVar& getGlobal1() 
     { 
      static MyVar global1; 
      return global1; 
     } 
     static MyVar& getGlobal2() 
     { 
      static MyVar global2; 
      return global2; 
     } 
     // .. etc 
} 
+3

Theo logic này, std :: cout là một mùi mã. –

+0

@ rr-: Bạn nên đọc [sách "Martin Fowlers" về tái cấu trúc] (http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672/ref=sr_1_1?ie=UTF8&qid= 1439219908 & sr = 8-1 & từ khóa = refactoring + bởi + martin + fowler). –

+0

@ rr-: Hoặc bất kỳ bài viết nào trong số hàng triệu bài viết về [Trạng thái có thể thay đổi toàn cục là xấu] (http://programmers.stackexchange.com/questions/148108/why-is-global-state-so-evil) –

0

Tôi thích cho phép singleton nhưng không thực thi nó để không bao giờ ẩn các nhà thầu và trình phá hủy. Điều đó đã được cho là chỉ hỗ trợ tôi.

Xoắn của tôi là tôi không sử dụng thường xuyên sử dụng hàm thành viên tĩnh trừ khi tôi muốn tạo một singleton thực và ẩn constr.Cách tiếp cận thông thường của tôi là:

template< typename T > 
T& singleton(void) 
{ 
    static char buffer[sizeof(T)]; 
    static T* single = new(buffer)T; 
    return *single; 
} 

Foo& instance = singleton<Foo>(); 

Tại sao không sử dụng trường hợp tĩnh của T thay vì vị trí mới? Ví dụ tĩnh cho phép đảm bảo trật tự xây dựng, nhưng không bảo đảm trật tự hủy diệt. Hầu hết các đối tượng bị phá hủy theo thứ tự ngược lại của xây dựng, nhưng các biến tĩnh và toàn cầu. Nếu bạn sử dụng phiên bản cá thể tĩnh, cuối cùng bạn sẽ nhận được các segfaults bí ẩn/liên tục, vv sau khi kết thúc chính.

Điều này có nghĩa là trình phá hủy đơn sẽ không bao giờ được gọi. Tuy nhiên, quá trình đi xuống và tài nguyên sẽ được khai hoang. Đó là một trong những khó khăn để có được sử dụng để nhưng tôi tin tưởng không có một giải pháp nền tảng chéo tốt hơn vào lúc này. May mắn thay, C++ 0x có một thay đổi được thực hiện để đảm bảo trật tự hủy diệt sẽ khắc phục vấn đề này. Khi trình biên dịch của bạn hỗ trợ chuẩn mới, chỉ cần nâng cấp hàm singleton để sử dụng một cá thể tĩnh.

Ngoài ra, tôi trong implemenation thực tế tôi sử dụng tăng để có được bộ nhớ liên kết thay vì một mảng nhân vật đơn giản, nhưng không muốn làm phức tạp ví dụ

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