2011-11-08 62 views
5

Tôi đang cố gắng triển khai mẫu nhà máy bằng cách đăng ký con trỏ hàm của lớp dẫn xuất đến nhà máy trong bản đồ tĩnh (thành viên của nhà máy) và tạo đối tượng nhìn lên bản đồ. Nhưng tôi nhận được một lỗi phân đoạn khi làm điều này.truy cập vào bản đồ tĩnh từ một hàm thành viên tĩnh - lỗi phân đoạn - C++

Đoạn Mã:

factory.cpp

typedef Shape* (*Funcptr)(); 

std::map<int,Funcptr> Factory::funcmap; 

int Factory::registerCreator(int ShapeID, Shape *(*CFuncptr)()) { 
    Factory::funcmap[ShapeID] = CFuncptr; 
return 1; 
} 

Shape* Factory::CreateObject(int ShapeID) { 
    std::map<int,Funcptr>::iterator iter; 
    iter = funcmap.find(ShapeID); 
    if(iter != funcmap.end()){ 
     return iter->second(); 
    } 
    return NULL; 
} 

factory.h

class Factory { 
public: 
    Factory(); 
    virtual ~Factory(); 
    static int registerCreator(int, Shape *(*CFuncptr)()); 
    Shape* CreateObject(int); 
private: 
    static std::map<int,Funcptr> funcmap; 
}; 

Square.cpp

static Shape *SquareCreator() { 
    return new Square; 
} 
static int SquareAutoRegHook = Factory::registerCreator(1,SquareCreator); 

On tạo đối tượng cho Nhà máy trong chính nộp một phân đoạn về lỗi xảy ra. Bạn có thể đề nghị nếu tôi làm sai điều gì đó. Tôi đang sử dụng CppUTest cho TDD và không chắc chắn làm thế nào để gỡ lỗi này.

+0

Hiển thị typedef cho 'Funcptr'. Bạn có thể viết lại mã của bạn để nó sử dụng typedef ở khắp mọi nơi (và kiểm tra)? Ngoài ra, tại sao 'SquareCreator()' khai báo 'tĩnh'? –

+0

@KerrekSB: vẫn không thay đổi. – Saaras

+0

Không liên quan, tôi thậm chí không chắc chắn bây giờ nếu con trỏ chức năng là tương thích covariantly. Tôi chỉ cố gắng tự mình thử nghiệm, và tôi chỉ bị lỗi "không thể chuyển đổi" được. –

Trả lời

7

Không đảm bảo về thứ tự tạo các đối tượng tĩnh được xác định trong các bản dịch khác nhau * do đó bạn có ảnh 50/50 như trước, phiên bản đầu tiên của Factory::funcmap hoặc Factory::registerCreator(1,SquareCreator) và Hành vi không xác định Russian Roulette không phải là một trò chơi hay để chơi.

Một cách tiếp cận chung để giải quyết vấn đề này và cách được mô tả trong mục 4 của the third edition of Scott Meyer's Effective C++ là sử dụng các đối tượng tĩnh cục bộ thay vì các đối tượng tĩnh chung. Trong trường hợp này nó có nghĩa là thay đổi Factory trông như thế này:

class Factory { 
public: 
    Factory(); 
    virtual ~Factory(); 
    static int registerCreator(int, Shape *(*CFuncptr)()); 
    Shape* CreateObject(int); 
private: 
    static std::map<int,Funcptr> & GetFactoryMap() { 
     static std::map<int,Funcptr> funcmap; 
     return funcmap; 
    } 
}; 

và thay đổi Factory::registerCreator này:

int Factory::registerCreator(int ShapeID, Shape *(*CFuncptr)()) { 
    GetFactoryMap()[ShapeID] = CFuncptr; 
    return 1; 
} 

cách funcmap này sẽ được khởi tạo lần đầu tiên registerCreator được gọi và sẽ không bao giờ được sử dụng chưa được khởi tạo .

* Hoặc, nói chung, các tệp .cpp khác nhau nếu bạn không quen với cụm từ dịch thuật

+0

Cảm ơn câu trả lời. – Saaras

+0

Tôi đã thử thủ thuật này và tôi đã có vấn đề với một số trình biên dịch nhiều funcmaps đang được tạo ra nếu bạn sử dụng các nhà máy trên các thư viện liên kết tĩnh và thực thi. Bây giờ tôi có một funcmap cho mỗi thư viện hoặc thực thi, nhưng chỉ khi nó được biên dịch trên một số hệ thống nhất định. – P1r4nh4

0

Hình như static initialization order fiasco. Dường như tại thời điểm SquareAutoRegHook được khởi tạo, funcmap chưa được tạo.

+0

Làm thế nào để đảm bảo thứ tự khởi tạo hoặc bất kỳ cách gọn gàng nào khác để thực hiện nó? – Saaras

+0

Điều đó có vẻ đúng. Nó cũng lạ rằng 'SquareCreator()' có liên kết bên trong nếu một con trỏ tới nó là bắt buộc trên toàn cầu. –

+0

@Saaras - bạn không thể đảm bảo thứ tự khởi tạo các đối tượng tĩnh, vì vậy hãy sử dụng các thường trình khởi tạo sẽ thực hiện cài đặt cho bạn khi được gọi từ 'main'. Khi 'main' bắt đầu, bạn có thể dựa vào tất cả các biến tĩnh được khởi tạo (theo thứ tự không xác định). – littleadv

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