2016-03-25 15 views
8

Giả sử rằng tất cả các lớp của một hệ thống phân cấp thực hiện chức năng thành viên mẫu g. Tất cả các lớp chia sẻ việc thực hiện cùng hai chức năng khác f1f2 mà gọi mẫu này:Gọi hàm đa hình không có mã trùng lặp

struct A { 
    virtual void f1() { 
     g(5); 
    } 
    virtual void f2() { 
     g(5.5); 
    } 
private: 
    template <typename T> void g(T) {std::cout << "In A" << std::endl;} 
}; 

struct B: A { 
    // Can I get rid of this duplicate code? 
    virtual void f1() { 
     g(5); 
    } 
    virtual void f2() { 
     g(5.5); 
    } 
private: 
    template <typename T> void g(T) {std::cout << "In B" << std::endl;} 
}; 

struct C: A { 
    // Can I get rid of this duplicate code? 
    virtual void f1() { 
     g(5); 
    } 
    virtual void f2() { 
     g(5.5); 
    } 
private: 
    template <typename T> void g(T) {std::cout << "In C" << std::endl;} 
}; 

int main() 
{ 
    B b; 
    A &a = b; 
    a.f1(); 
    return 0; 
} 

Kể từ khi triển khai của f1f2 là giống hệt nhau trong tất cả các lớp học, làm thế nào tôi có thể thoát khỏi các mã trùng lặp và vẫn có các cuộc gọi đa hình trong main hoạt động như mong đợi (tức là tạo ra đầu ra "Trong B")?

Trả lời

3

Lưu ý rằng việc triển khai của f1f2 trong A, B, và Ckhông giống hệt nhau. Hãy hạn chế nó để f1 s. Một hàm gọi hàm ::A::g<int>, một hàm khác gọi hàm ::B::g<int> và hàm thứ ba gọi hàm là ::C::g<int>. Chúng là rất xa từ giống hệt nhau.

Điều tốt nhất bạn có thể làm là có một cơ sở CRTP kiểu:

template <class Derived> 
struct DelegateToG : public A 
{ 
    void f1() override 
    { 
    static_cast<Derived*>(this)->g(5); 
    } 

    void f2() override 
    { 
    static_cast<Derived*>(this)->g(5.5); 
    } 
}; 

class B : public DelegateToG<B> 
{ 
    friend DelegateToG<B>; 
private: 
    template <class T> void g(T) { /*...*/ } 
}; 

class C : public DelegateToG<C> 
{ 
    friend DelegateToG<C>; 
private: 
    template <class T> void g(T) { /*...*/ } 
}; 
+0

Tôi nhìn theo cách khác: tất cả đều gọi 'this-> g ', trong đó' * this' là loại động của 'a'. Tại sao đây là cách nhìn sai? – AlwaysLearning

+1

@AlwaysLearning Vì 'g' không phải là (và không thể là) ảo, có nghĩa là các quy tắc biên dịch bình thường áp dụng cho nó. Lưu ý rằng các quy tắc biên dịch thời gian là mặc định trong C++. Chỉ có một vài ngoại lệ rõ ràng được chọn cho điều này ('virtual',' dynamic_cast', 'typeid'). – Angew

+0

Phải. Nhưng 'f1' là ảo. Trong 'f1' kiểu' this' này là kiểu động của 'a'. Tất cả những gì tôi muốn là phải thực hiện giống hệt nhau của 'f1' trong lớp dẫn xuất mà không cần phải lặp lại việc thực hiện ... – AlwaysLearning

1

Bạn chỉ có thể yếu tố ra những điều tầm cỡ cụ thể mà mẫu chức năng sử dụng, chẳng hạn như (trong ví dụ của bạn) tên lớp :

#include <iostream> 
using namespace std; 

class A 
{ 
private: 
    virtual auto classname() const -> char const* { return "A"; } 

protected: 
    template <typename T> void g(T) {cout << "In " << classname() << endl;} 

public: 
    virtual void f1() { g(5); } 
    virtual void f2() { g(5.5); } 
}; 

class B 
    : public A 
{ 
private: 
    auto classname() const -> char const* override { return "B"; } 
}; 

class C 
    : public A 
{ 
private: 
    auto classname() const -> char const* override { return "C"; } 
}; 

auto main() 
    -> int 
{ static_cast<A&&>(B()).f1(); } 
+0

Đây chỉ là một ví dụ. Thực tế, việc triển khai 'g' thay đổi rất nhiều. – AlwaysLearning

+0

@AlwaysLearning: Tôi không thể trả lời câu hỏi mà bạn chưa đặt ra. Lấy làm tiếc. Không telepathic. –

+0

Bạn hoàn toàn đúng. – AlwaysLearning

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