2010-11-26 67 views
6

Tôi đang cố gắng thực hiện một số cơ chế trong C++ theo đó tất cả các lớp bắt nguồn từ một lớp cơ sở chung được gán một "lớp ID" duy nhất. Ví dụ:TypeID cho các lớp dẫn xuất của một lớp cơ sở chung

class BaseClass 
{ 
    //... 
    public: unsigned int GetID(void); 
    //... 
}; 
class DerivedClass : public BaseClass 
{ 
} 

Lớp DerivedClass, và tất cả những đứa trẻ khác của BaseClass, sẽ có thể quay trở lại định danh duy nhất mà không cần bất kỳ mã bổ sung thêm vào DerivedClass ... C++ đang thực hiện này khá khó khăn đối với tôi, tuy nhiên. Có những câu chuyện mới trên trang chủ.

Cảm ơn trước! --- Dan

+0

ID phải là int hoặc có thể là bất kỳ loại duy nhất nào, ví dụ: cái được trả về bởi typeid. Là BaseClass Polymorphic của bạn? – Chubsdad

+0

Lớp cơ sở của tôi IS đa hình, nhưng do tôi không có khả năng tìm được giải thích rõ ràng và kỹ lưỡng về bản chất chính xác của chi phí đi kèm với RTTI (cả về hiệu năng lẫn trí nhớ), tôi đang cố gắng tránh nó hoàn toàn . – Dan

Trả lời

2

Bạn không cho biết rằng bạn đã quen thuộc với typeiddynamic_cast.

Cơ hội là chúng giải quyết được sự cố của bạn.

Nếu không, vui lòng mô tả lý do tại sao không.

Chúc mừng & hth.,

+0

Đó không phải là một bình luận? – Chubsdad

+0

Xin lỗi, quên đề cập: Cố gắng tránh RTTI nếu có thể, vì tôi đã nghe các câu chuyện khác nhau về mức phí mà nó đòi hỏi và không muốn mạo hiểm, vì hiệu suất rất quan trọng đối với ứng dụng của tôi. – Dan

+0

@Chubstad: không, nó không phải là một bình luận. 'typeid' là câu trả lời chung khi bạn muốn một id loại. bạn thậm chí có thể suy ra điều đó từ tên. –

1

Như Alf nói, điều này không cần thiết. typeid đã cung cấp mã định danh lớp duy nhất, mặc dù số nhận dạng không phải là số nguyên. Chỉ cần cho cười, nếu tôi cho phép để thư giãn "lớp cơ sở chung" điều kiện sau đó:

inline unsigned int counter() { 
    static unsigned int count = 0; 
    return ++count; 
} 

struct BaseClass { 
    virtual unsigned int GetID() = 0; 
    virtual ~BaseClass() {} 
}; 

template <typename D> 
struct IntermediateClass : BaseClass { 
    virtual unsigned int GetID() { 
     static unsigned int thisid = counter(); 
     return thisid; 
    } 
}; 

// usage 
struct Derived : IntermediateClass<Derived> { 
    ... 
}; 

Bạn sẽ cần phải thêm thread-an toàn trong counter nếu nó được sử dụng trong các chương trình đa luồng.

Rõ ràng ID chỉ là duy nhất trong một lần chạy chương trình nhất định. Nếu bạn có rất nhiều nhà xây dựng với các chữ ký khác nhau cho các lớp khác nhau, vì bạn cần chèn lớp trung gian giữa mỗi lớp dẫn xuất và lớp cơ sở trực tiếp của nó. Nhưng bạn luôn có thể giải cứu tất cả những gì như sau:

inline unsigned int counter() { 
    static unsigned int count = 0; 
    return ++count; 
} 

struct BaseClass { 
    virtual unsigned int GetID() = 0; 
    virtual ~BaseClass() {} 
}; 

template <typename D> 
unsigned int ThisID(const D *) { 
    static unsigned int thisid = counter(); 
    return thisid; 
} 

// usage 
struct Derived : BaseClass { 
    // this single line pasted in each derived class 
    virtual unsigned int GetID() { return ThisID(this); } 
    ... 
}; 

Tôi đoán có một "cơ hội" cho một tính năng ngôn ngữ mới tại đây: một hàm ảo được định nghĩa trong lớp cơ sở như một mẫu chức năng với một " typename "tham số mẫu và được tự động ghi đè trong mỗi lớp dẫn xuất bằng cách sử dụng lớp dẫn xuất đó làm đối số mẫu. cú pháp tưởng tượng, vì chức năng mẫu ảo là bất hợp pháp:

struct BaseClass { 
    template <typename Derived> 
    virtual unsigned int GetID() { 
     static unsigned int thisid = counter(); 
     return thisid; 
    } 
    virtual ~BaseClass() {} 
}; 

cứng để biện minh cho một tính năng ngôn ngữ trên cơ sở muốn tái thực hiện RTTI mình, tâm trí ...

+0

Điều này hoạt động tốt, nhưng tôi mất tính đa hình của lớp của tôi vì hai lớp dẫn xuất hiện đang kế thừa từ hai lớp khác nhau. – Dan

+0

@Dan: Tôi đã cười khúc khích một chút kể từ khi bạn nói rằng các lớp học của bạn là đa hình. Trong tùy chọn đầu tiên, mọi thứ được dẫn xuất gián tiếp từ BaseClass, vì vậy bạn vẫn có thể sử dụng các đối tượng Nguồn gốc thông qua BaseClass *. Trong tùy chọn thứ hai, mọi thứ được lấy trực tiếp từ BaseClass, điều này làm cho việc quản lý các hàm tạo dễ dàng hơn nhiều, nhưng bạn phải thêm một hàm (giống hệt) vào mỗi lớp dẫn xuất. –

+0

Chuột, chỉ nhận ra dòng trong mỗi lớp dẫn xuất không giống nhau - nó phải đề cập đến lớp để phân biệt cuộc gọi trong trường hợp một trong các lớp dẫn xuất này mở rộng một lớp khác và do đó thừa hưởng hai hàm GetIDImpl. Tôi sẽ thay đổi tùy chọn đó. –

0

này đòi hỏi phải sửa đổi các lớp Derived , nhưng nếu thiếu tự tin trong RTTI là lý do duy nhất để tránh tuyến đường được thiết lập tốt của typeid, dynamic_cast, THEN

Một cái gì đó như thế này nên là một cược tốt. Nó cũng trả về một 'int' so với 'type_info' của RTTI.

class BaseClass{ 
    //... 
    public: 
     virtual unsigned int GetID(void); 
    //... 
}; 

class DerivedClass : public BaseClass{ 
public: 
    virtual unsigned int GetID(void); 
}; 
+0

Tôi đang cố gắng tránh triển khai riêng biệt cho mỗi DerivedClass; đó là vấn đề của tôi. Xin lỗi nếu tôi không làm rõ điều đó. – Dan

2

Bạn nên lắng nghe Alf :) Dưới đây là phân tích của tôi: trong thế giới tinh khiết, xác định triển khai đe dọa chức năng ảo đa hình, không thể được yêu cầu và không nên cần thiết.

Trong thế giới bẩn của chương trình thực tế, bạn có thể có một số lý do cho nhận dạng duy nhất như marshaling dữ liệu vào đĩa, xác định các thông điệp chẩn đoán, truy tìm dòng điều khiển, tích lũy thống kê sử dụng vv

Nếu suy nghĩ của bạn là tinh khiết , thiết kế của bạn là sai. Đi xa và tìm ra lý do tại sao bạn không thể yêu cầu một id duy nhất.

Nếu suy nghĩ của bạn bị hỏng bởi thực tế, thì bạn đã sẵn sàng trả giá hiệu suất và bộ nhớ để đáp ứng yêu cầu của bạn, và sau đó theo đặc điểm kỹ thuật chi phí sử dụng các tính năng ngôn ngữ được xây dựng trong là cách duy nhất để đạt được mục tiêu cung cấp dịch vụ nhận diện không xâm lấn. Bởi không xâm lấn tôi có nghĩa là bạn không cần phải thêm bất cứ điều gì cho mỗi lớp dẫn xuất. Rõ ràng một cái gì đó phải được thêm vào, vì vậy nếu bạn không muốn làm như vậy, bạn có ít sự lựa chọn nhưng để chấp nhận rằng trình biên dịch bổ sung cho bạn.

Lưu ý chính ở đây là nếu bạn đang sử dụng thư viện được tải động (DLLS) RTTI có thể không hoạt động như mong đợi. Điều này không chỉ ảnh hưởng đến typeid bất lợi, nó cũng có thể ngăn chặn bắt ngoại lệ bạn mong đợi để bị bắt (tôi đã bị cắn!). Một số dịch vụ chăm sóc có thể cần thiết để đảm bảo vtables và RTTI khác được tạo duy nhất. Điều này có nghĩa là, ví dụ, nếu vtables hook trên destructor của bạn, nó không phải là inline, bởi vì nó có thể được sinh ra ở nhiều nơi trong trường hợp đó, phá hủy tính duy nhất. Một số hack về có thể cần thiết ở đây trong trường hợp không hỗ trợ tiêu chuẩn ISO cho tải động.

+0

Bạn dường như biết khá nhiều về các hoạt động bên trong của RTTI. Có bất kỳ nơi nào trên mạng mà bạn biết về điều đó có mô tả kỹ lưỡng về các cơ chế được sử dụng ở cấp trình biên dịch để triển khai tính năng này không? – Dan

+0

Tôi không có các liên kết tiện dụng, nhưng những nơi cần xem là các trang web phát triển gcc, và các thông số kỹ thuật của ABI, ban đầu được AMD sản xuất nhưng hiện được quản lý bởi một nhóm ngành. Điều đó về cơ bản đưa ra các API được sử dụng cho bộ vi xử lý x86_64 cho các tính năng C++ khác nhau bao gồm cả đối số đi qua trong sổ đăng ký, xử lý ngoại lệ, liên kết động vv ... – Yttrill

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