2013-03-20 34 views
13

Tôi có một chuỗi kế thừa với Base là lớp cơ sở. Tôi muốn có thể viết một mẫu lớp mà thừa hưởng Base và có thể một lớp Base-derived khác. Tôi có thể sử dụng thừa kế ảo, nhưng tôi tìm thấy một giải pháp khác. Tôi muốn biết nếu thiết kế lớp phổ biến/đáng kể/hợp pháp:Thiết kế C++ OO: Thừa kế tham số mẫu

Viết mẫu lớp trong đó thông số mẫu là lớp bắt nguồn từ đó, tức là lớp phải là Lớp gốc hoặc Gốc. Trong hàm tạo, tôi có thể sử dụng khẳng định tĩnh để thực sự đảm bảo rằng người dùng không sử dụng bất kỳ lớp bất hợp pháp nào làm tham số mẫu.

Nếu nó hoạt động, tôi sẽ không bao giờ có vấn đề thừa kế ảo ... câu hỏi là, nó là ok để làm điều đó. Tôi chưa bao giờ thấy nó trong các dự án khác, vì vậy tôi muốn chắc chắn trước khi tôi sử dụng nó.

EDIT: Chỉ cần chắc chắn tôi không làm bạn bối rối, sau đây là một số mã:

class Base 
{ 
}; 

class Derived : public Base 
{ 
}; 

template <Class TheBase> 
class MyDerived : public TheBase 
{ 
}; 

Bây giờ tôi có thể sử dụng Base hay bất kỳ lớp -derived Base, ví dụ Derived, như tham số TheBase.

+5

Bạn có thể cho chúng tôi thấy một số mã không? Tôi nghĩ rằng nó sẽ giúp đỡ để có được câu trả lời tốt hơn. –

+1

Thành thật mà nói, có vẻ như bạn mô tả [Mẫu mẫu tò mò định kỳ (CRTP)] (http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern). – WhozCraig

+2

"Tôi sẽ không bao giờ có vấn đề thừa kế ảo" ... Bạn đã bao giờ nghe nói về luật của Murphy chưa? :) –

Trả lời

10

Chỉnh sửa sau: Một năm sau, tại đây tôi đang sửa lại câu trả lời của riêng mình. Ban đầu tôi đã tuyên bố sai rằng mẫu OP được đăng là CRTP. Điều này LAF không đúng. Nó thực sự là một mixin, xin vui lòng đọc câu trả lời của Daniel Mahler thấp hơn trên trang cho lời giải thích chính xác.

Bản gốc: Bạn có thể sử dụng thiết kế như vậy. Ví dụ: WTL sử dụng nó. Nó được sử dụng để thực hiện Static Polymorphism và được gọi là Curiously recurring template pattern

+1

Không chính xác CRTP, nhưng vẫn hữu ích. Nó là tốt cho việc lựa chọn giao diện thay thế, ví dụ. – ThomasMcLeod

5

Đây là phương châm tốt: Sử dụng templating cho các loại, nhưng thừa kế cho hành vi.

Dính vào nó. Chắc chắn có rất nhiều vết cắt/thủ thuật ngắn mà bạn có thể sử dụng để hoàn thành công việc, nhưng về lâu dài, những lựa chọn thiết kế xấu này sẽ gây đau đầu. Nếu bạn muốn sử dụng như vậy, hãy chắc chắn để nghiên cứu những lợi ích và hạn chế.

Bây giờ, quay trở lại câu hỏi của bạn, những gì bạn đã yêu cầu có thể làm: xem CRTPStatic polymorphism.

+1

+1 điểm rất tốt! – Walter

+1

-1. Bằng phương châm đó, chúng tôi sẽ không có thiết kế dựa trên chính sách. Không phải là một điểm tốt sau khi tất cả. –

+0

Nó không phải là nói 'không bao giờ sử dụng thiết kế như vậy', nó là để khuyến khích mọi người suy nghĩ lại sự lựa chọn của họ. Điều này liên quan đến các mô hình thiết kế được thiết lập tốt như thiết kế Chính sách. Bạn hoàn toàn mất tích điểm. – meyumer

7

Điều này là tốt, như Zadirion chỉ ra. Lý do nó hoạt động (đơn giản) là các mẫu trong C++, không giống như các generic trong C#, là thời gian biên dịch. Tôi sẽ nói rằng "đó là một typedef" và tôi sẽ nhận được rất nhiều điều cho nó, nhưng hãy giữ nó đơn giản và nói nó là như vậy.

xem xét:

class base { 
protected: 
    base() { }; 
    virtual ~base() { }; 
}; 

template<class T> 
class super : public T { 
}; 

và sau:

super<base> s; 

Tuyệt đối tốt. Đây thực sự là một công trình khá đẹp. Bởi vì nó là thời gian biên dịch, bạn có thể chọn lớp cơ sở của bạn, mà trong một số thành ngữ thiết kế có thể rất thuận lợi.

+2

+1 để xem chi tiết hơn một chút so với tôi. Tôi sẽ thêm rằng đây là một mẫu khá hữu ích khi bạn cần viết một số mã có thể tái sử dụng như một lớp cơ sở mà mọi người có thể lấy được, nhưng một số mã sẽ không thể hoạt động với các lớp có nguồn gốc của một số kiểu nhất định. Trong trường hợp đó, mô hình có thể được sử dụng trong lớp cơ sở để xác định thuộc tính nào có nguồn gốc (chẳng hạn như std :: is_base_of) và cho phép mã nhất định khi các điều kiện nhất định được đáp ứng. –

+1

Cảm ơn bạn đã trả lời! Tôi đã thêm một số mã trong bài đăng để đảm bảo mô tả của tôi không gây nhầm lẫn cho mọi người – cfa45ca55111016ee9269f0a52e771

18

Đây là mẫu thiết kế hợp lệ. Đó là di sản mixin, không phải CRTP.Sự thừa kế Mixin cung cấp một cách để mô phỏng đa thừa kế một cách an toàn bởi lập trình viên theo cách thủ công tuyến tính hệ thống phân cấp thừa kế. Các lớp templated là mixins. Nếu bạn muốn mở rộng một lớp với nhiều mixin, bạn phải quyết định thứ tự của bố cục như Big<Friendly<Dog> >. Lập trình Mixin trong C++ được mô tả trong số Dr Dobb's article này. Mixins có thể được sử dụng để triển khai phiên bản tĩnh mẫu trang trí GoF như mô tả here. Mixins đóng một vai trò simlar trong C++ rằng các đặc điểm (không phải đặc điểm C++) chơi trong Scala & SmallTalk.

Trong CRTP nó là lớp cơ sở đó là một mẫu:

template <class Param> 
class Base { ... }; 

class Derived : public Base<Derived> { ... }; 
+1

Thực ra không phải CRTP. Cảm ơn vì đã chỉ ra, Daniel Mahler. Tôi đã sửa đổi câu trả lời của riêng mình, tôi không phải là một fan hâm mộ lớn của những người gây hiểu nhầm. –

1

Những gì bạn đang cố gắng làm là để kế thừa từ hai lớp có thể có một lớp cơ sở chung, đó là đúng? Trong trường hợp đó bạn shoul đối phó với các vấn đề thừa kế ảo (tức là bạn sẽ phải khai báo là virtual thừa kế của lớp cơ sở cho cả hai lớp bạn quan tâm). Điều đó sẽ chỉ gây ra một chi phí nhỏ (có thể không đáng kể) do một số hỗ trợ thời gian chạy (2 vpointers hơn).

Mã của bạn không phải là CRTP (trong CRTP lớp cơ sở là một lớp được nhận lớp dẫn xuất) và dường như không giải quyết theo bất kỳ cách nào vấn đề thừa kế đôi mà bạn đang cố gắng loại bỏ. Theo như tôi có thể thấy, bạn có thể chấp nhận thừa kế ảo và sử dụng từ khóa ảo phát sinh trong chi phí tối thiểu hoặc bạn có thể cấu trúc lại mã của mình.

Tôi không hoàn toàn hiểu những gì bạn đang cố gắng làm nhưng nếu bạn đang cố gắng kế thừa từ hai lớp khác nhau với một lớp cơ sở chung (thừa kế ảo là tất cả về điều này) và vì lý do nào đó bạn không muốn sử dụng từ khóa ảo, sau đó bạn có thể sử dụng CRTP trong thời trang sau đây:

#include <iostream> 
using namespace std; 

template<class Derived> 
class Base 
{ 
public: 
    void basefunc() { cout << "base here"<< endl; } 
    virtual void polyfunc() { cout << "base poly here"<< endl; } 
}; 

class Derived : public Base<Derived> 
{ 
public: 
    void derivedfunc() { cout << "derived here"<< endl; } 
    virtual void polyfunc() { cout << "derived poly here"<< endl; } 
}; 

class OtherDerived : public Base<OtherDerived> 
{ 
public: 
    void otherderivedfunc() { cout << "otherderived here"<< endl; } 
    virtual void polyfunc() { cout << "otherderived poly here"<< endl; } 
}; 

class InheritingFromBoth : public Derived, public OtherDerived 
{ 
public: 
    void inheritingfunc() { cout << "inheritingfromboth here" << endl; } 
    virtual void polyfunc() { cout << "inheritingfromboth poly here"<< endl; } 
}; 

int main() { 

    Derived obj; 
    OtherDerived obj2; 

    InheritingFromBoth *obj3 = new InheritingFromBoth(); 
    Derived *der = dynamic_cast<Derived*>(obj3); 
    der->polyfunc(); 
    OtherDerived *der2 = dynamic_cast<OtherDerived*>(obj3); 
    der2->polyfunc(); 

    Base<Derived>* bptr = dynamic_cast<Base<Derived>*>(obj3); 
    bptr->polyfunc(); 
    Base<OtherDerived>* bptr2 = dynamic_cast<Base<OtherDerived>*>(obj3); 
    bptr2->polyfunc(); 



    return 0; 
} 

bằng cách tạo ra hai trường hợp khác nhau của các lớp cơ sở bạn sẽ tránh mập mờ thừa kế.

A, có lẽ sạch hơn, giải pháp đơn giản hơn nếu bạn có ý định kế thừa từ một cơ sở và một lớp cơ sở có nguồn gốc từ cùng một lúc như sau:

  • Kế thừa từ một cơ sở lớp từ cơ sở có nguồn gốc từ lớp làm cho tôi nghĩ rằng bạn muốn để có thể truy cập vào các phương pháp cơ bản và phương pháp cơ sở có nguồn gốc từ cùng một lúc ..

nếu bạn chú ý trong thiết kế lớp học của bạn để tiềm năng tên ẩnproblems và chỉ sử dụng đa hình để "tùy chỉnh" hành vi của các hàm bạn thực sự muốn khác (và có thể được điều khiển bằng cast), sau đó một hệ thống phân cấp rõ ràng là Base -> Derived -> YourClass cuối cùng có thể giải quyết được vấn đề của bạn.

Trong trường hợp cụ thể, cách tiếp cận của bạn hoạt động, như được ghi nhận bởi những người khác đã được sử dụng trong nhiều ứng dụng nhưng tôi không nghĩ có thể giải quyết vấn đề thừa kế của bạn một cách hiệu quả. Cuối cùng, chỉ trường hợp thiết kế cụ thể mới có thể dẫn đến giải pháp ít độc ác hơn.