2012-02-01 30 views
5

Interface:C++ Templates polymorphism trở ngại

template <class T> 
class Interface{ 
    public: 
    typedef T Units; 
    virtual T get() = 0; 
}; 

Implementation1:

class Implementation1: public Interface<float> { 
    public: 

    float get() { 
     return 0.0f; 
    } 

}; 

Implementation2:

class Implementation2: public Interface<int> { 
    public: 

    int get() { 
     return 0; 
    } 

}; 

container (có lỗi):

class Container{ 
    private: 
    Interface* floatGetter; 
    int n; 
    Timer::Units* array; 

    public: 
    Container(Interface* floatGetter, int n) { 
     this->floatGetter= floatGetter; 
     this->n = n; 
     array = new Timer::Units[n]; 
    } 

    ~Container() { 

    } 

}; 

Để biết thêm chi tiết, tôi có giao diện mẫu và lớp dẫn xuất từ ​​giao diện này không có mẫu. Một số lớp khác lấy một đối tượng của lớp dẫn xuất nhưng nó lấy đối tượng làm giao diện (nói cách khác, tiêm phụ thuộc). Nhưng loại giao diện trong lớp này được xác định bằng cách thực hiện giao diện. Làm thế nào để thực hiện ý tưởng này trong C++?

Edit1:

Ví dụ:

Interface<float> myInterface1 = new Implementation1(); 
Interface<int> myInterface2 = new Implementation2(); 
Container container1 = new Container(myInterface1, 10); 
Container container2 = new Container(myInterface2, 10); 

Tôi cần chứa mà hiểu giao diện mẫu luận việc triển khai này.

Trả lời

6

OK, trước tiên, giải thích sự cố tại đây. Những gì được yêu cầu là một giao diện, định nghĩa một phương thức ảo, được sử dụng để lấy một giá trị với một kiểu templated. Vì những gì chúng ta muốn là một giao diện, phương thức get phải là ảo. Mặt khác, chúng tôi muốn có thể trả về các loại khác nhau, vì vậy chúng tôi muốn tạo thành nó. Tuy nhiên, một phương pháp ảo không thể được templetized, bởi vì trình biên dịch sẽ không biết những instantions của phương pháp đó để bao gồm trong vtable.

Một giải pháp là làm những gì được thực hiện trong câu hỏi, tức là templetize lớp giao diện. Một thuộc tính quan trọng của các kiểu mẫu là các instantiations khác nhau của cùng một lớp là các kiểu hoàn toàn khác nhau. Họ không chia sẻ một cơ sở chung, và họ không chuyển đổi được với nhau. Chúng tôi chỉ đơn giản là không thể có một con trỏ Interface<Generic> đi xung quanh trong các chức năng thường xuyên, với phương thức get() của chúng được gọi. Hãy xem xét điều này: Mỗi phần tử của loại mẫu Giao diện có chữ ký khác nhau cho phương thức get(). Điều này có nghĩa rằng trong khi phương pháp đó đang được gọi, những thứ khác nhau phải xảy ra trên stack. Làm thế nào trình biên dịch có thể biết phiên bản nào của phương thức get() để gọi (cách chuẩn bị ngăn xếp cho cuộc gọi hàm) nếu tất cả nó có là một con trỏ Interface<Generic>.

Tôi có thể nghĩ đến hai giải pháp chung cho vấn đề đó.

  1. Xóa tất cả mẫu mumbo-jumbo và làm cho phương thức get() trả về một đối tượng loại đã xóa, chẳng hạn như boost :: variant hoặc boost :: any. Sửa tôi nếu tôi sai ở đây (*), nhưng tăng :: biến thể giống như một công đoàn nhớ loại liên kết nào được gán, trong khi boost :: bất kỳ giống như void *, nhưng nó nhớ loại nó trỏ đến . Đường dẫn giải pháp này ngụ ý hai điều: a) Các loại đối tượng được trả về sẽ được giải quyết trong thời gian chạy, và sẽ có một số chi phí trong khi thao tác các loại này. b) Các lớp con của Giao diện sẽ phải quản lý một trong các đối tượng loại đã bị xóa này, làm cho chúng trở nên phức tạp hơn.

  2. Lấy mẫu mumbo-jumbo đến mức cực đại và tham khảo đối tượng Giao diện luôn trong ngữ cảnh được sắp xếp theo thứ tự, để trình biên dịch tạo ra các cuộc gọi hàm đúng trong các cảnh báo của các ngữ cảnh đó. Tôi đưa ra một ví dụ dưới đây theo con đường này. Ví dụ này tạo một vùng chứa để chứa các loại đối tượng Giao diện <> khác nhau, đồng thời cho phép áp dụng các hàm chức năng (được gọi là "khách truy cập" chung) đúng không? Lưu ý rằng trong ví dụ đó, các đối tượng Giao diện với các tham số kiểu khác nhau thực sự được lưu giữ trong các danh sách std :: khác nhau trong lớp chứa đó, vì vậy trong thời gian chạy, không cần phải giải quyết các kiểu của chúng.

Disclaimer: Sau đây là một overkill ...

Đây là cách bạn có thể có một container của "giao diện" lớp mẫu với đối số mẫu khác nhau. Tôi đã sử dụng một std :: danh sách để giữ các trường hợp, nhưng bạn có thể thay đổi nó.

#include<boost/fusion/container/vector.hpp> 
#include<boost/fusion/algorithm.hpp> 
#include<boost/mpl/transform.hpp> 
#include<boost/mpl/contains.hpp> 
#include<boost/utility/enable_if.hpp> 
#include<boost/type_traits/add_reference.hpp> 
#include<list> 
#include<algorithm> 
#include <iostream> 

using namespace boost; 

template <class T> 
class Interface{ 
    public: 
    typedef T Units; 
    virtual T get() = 0; 
}; 

class Implementation1: public Interface<float> { 
    public: 

    float get() { 
     return 0.0f; 
    } 

}; 

class Implementation2: public Interface<int> { 
    public: 

    int get() { 
     return 5; 
    } 

}; 

template<class element> 
struct to_list { 
    typedef std::list<Interface<element> *> type; 
}; 

template<class elementVector> 
struct to_containers { 
    typedef typename mpl::transform<elementVector,to_list<mpl::_1> >::type type; 
}; 

class Container{ 
    typedef fusion::vector<int,float> AllowedTypes; 
    typename to_containers<AllowedTypes>::type containers; 

public: 
    template<class type> typename enable_if<mpl::contains<AllowedTypes,type>,void>::type 
    /*void*/ add(Interface< type/*included in AllowedTypes*/ > & floatGetter) { 
     fusion::deref(fusion::find<typename to_list<type>::type >(containers)) 
      /*<type> container*/.push_back(&floatGetter); 
    } 

    template<class functional> 
    void apply(functional f) { 
     fusion::for_each(containers,applyFunctional<functional>(f)); 
    } 

private: 
    template<class functional> 
    struct applyFunctional { 
     functional f; 
     applyFunctional(functional f): f(f){} 
     template<class T> void operator()(T & in) const { 
      std::for_each(in.begin(), in.end(),f); 
     } 
    }; 

}; 

struct printValueFunctional { 
    template<class element> 
    void operator()(Interface<element> * in) const { 
     std::cout<<"Hi, my value is:"<<in->get()<<"\n"; 
    } 
}; 

int main() { 

    Implementation1 impl1; 
    Implementation2 impl2; 
    Interface<float> &myInterface1 = impl1; 
    Interface<int> &myInterface2 = impl2; 
    Container container; 
    container.add(myInterface1); 
    container.add(myInterface2); 
    container.apply(printValueFunctional()); 
    return 0; 
} 

Và kết quả là:

Hi, my value is:5 
Hi, my value is:0 

Vâng, đây thực sự là một quá mức cần thiết rất lớn đối với hầu hết các ứng dụng, nhưng bạn hỏi cho nó :)

Nếu bạn chỉ muốn một giao diện, mà có thể trả về những thứ khác nhau, bạn cũng có thể xem xét boost.variant. Ví dụ trên thực sự có giá trị đối với tất cả tính đa hình tĩnh mà nó sử dụng.

CHỈNH SỬA: David đã chỉ ra một cái gì đó quan trọng, nó có thể là một cạm bẫy, nếu bạn, vì lý do nào đó, giả sử khác. Vùng chứa này không thực sự đúng với thứ tự của mục chèn. Thứ tự của các cuộc gọi chức năng của bạn có thể không xảy ra theo thứ tự chèn các mục, tức là, giả sử rằng lần lặp lại sẽ theo thứ tự "ngẫu nhiên".

(*) tăng :: biến thể và tăng :: bất kỳ được thảo luận here

+1

+1 cho một đoạn siêu lập trình tốt. Tôi không nghĩ rằng đó là một giải pháp tốt cho vấn đề, nhưng nó xứng đáng với đại diện :) –

+0

Cảm ơn :) Tôi không nghĩ rằng đó là một giải pháp tốt cho vấn đề nói chung, nhưng nó chỉ cho thấy rằng lập trình meta mẫu cho phép điều này mà không cần xóa. Bạn cũng nhận được một container hỗn hợp với quá trình lặp lại rất nhanh. – enobayram

+0

Nó không thực sự là một container hỗn hợp (hoặc là nó?) ... nhưng một loại chứa nhiều container trong nội bộ. Đối với tôi, sự khác biệt là trong thực tế là các kiểu khác nhau vẫn được tách riêng trong nội bộ, ngay cả khi bạn có ấn tượng rằng chúng không có, và điều đó có nghĩa là trong khi loại xóa, bạn có thể duy trì các bất biến của thùng chứa (ví dụ, thứ tự chèn trong các thùng chứa tuần tự), bạn không thể làm tương tự với cách tiếp cận này (thành thật mà nói, đây chỉ là linh cảm, tôi đã đọc qua mã, nhưng chưa biên dịch/thử nó) –

4

Interface là mẫu, không phải là một loại. Các biến trong lớp của bạn phải là sự khởi tạo của mẫu với một loại cụ thể, như:

class Container { 
    Interface<float> *floatGetter; 

Và tương tự như đối số cho hàm tạo.

Lưu ý bên: trình phá hủy của bạn nên giải phóng các tài nguyên mà lớp của bạn xử lý.

Lưu ý phụ 2: rất khó để viết loại trực tiếp quản lý nhiều tài nguyên, hãy cân nhắc sử dụng con trỏ thông minh để giữ dữ liệu của bạn.

Lưu ý bên 3: tìm hiểu và sử dụng danh sách khởi tạo.

+0

* constructor * của bạn nên giải phóng tài nguyên? –

+0

@jesse cảm ơn vì đã đánh bắt được lỗi đánh máy ... Tất nhiên, trình phá hủy sẽ giải phóng tài nguyên, chứ không phải là hàm tạo. –

+0

Hãy tìm bản cập nhật, xin vui lòng – itun

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