Kế thừa từ một lớp mẫu trong c ++


106

Giả sử chúng ta có một lớp mẫu Area, có một biến thành viên T area, a T getArea()và một void setArea(T)hàm thành viên.

Tôi có thể tạo một Areađối tượng của một loại cụ thể bằng cách gõ Area<int>.

Bây giờ tôi có một lớp Rectanglekế thừa Arealớp. Vì Rectanglebản thân nó không phải là một mẫu, tôi không thể nhập Rectangle<int>.

Làm cách nào để tôi chuyên biệt hóa Areakiểu kế thừa cho Rectanglecác đối tượng?

CHỈNH SỬA: Xin lỗi, tôi đã quên làm rõ - câu hỏi của tôi là liệu có thể kế thừa Khu vực mà không chuyên biệt hóa nó hay không, vì vậy nó không được kế thừa như Khu vực số nguyên nhưng như Hình chữ nhật Khu vực có thể chuyên biệt hóa các kiểu.


3
Đó là mẫu lớp , vì nó là mẫu mà từ đó các lớp được tạo ra.
sbi

1
@sbi Tôi không có ý định bắt đầu cuộc chiến nảy lửa ở đây, nhưng nếu Bjarne Stroustrup không tạo ra sự khác biệt giữa lớp mẫulớp mẫu (xem Ngôn ngữ lập trình C ++ , ấn bản thứ 4, phần 23.2.1), thì bạn không nên cũng không.
Michael Warner

@MichaelWarner Tôi dường như nhớ anh ấy đã tạo ra sự khác biệt. Tuy nhiên, đó là vào những năm 90 trên Usenet. Có lẽ anh ấy đã bỏ cuộc kể từ đó. (Hoặc có lẽ ông đề cập đến một lớp mẫu được thuyết minh như một lớp học mẫu?)
SBI

Câu trả lời:


244

Để hiểu được các mẫu, việc hiểu rõ thuật ngữ một cách thẳng thắn sẽ có lợi rất nhiều vì cách bạn nói về chúng quyết định cách nghĩ về chúng.

Cụ thể, Areakhông phải là một lớp mẫu, mà là một mẫu lớp. Đó là, nó là một khuôn mẫu mà từ đó các lớp có thể được tạo ra. Area<int>là một lớp như vậy (nó không phải là một đối tượng, nhưng tất nhiên bạn có thể tạo một đối tượng từ lớp đó theo cách giống như cách bạn có thể tạo các đối tượng từ bất kỳ lớp nào khác). Một lớp học như vậy sẽ là Area<char>. Lưu ý rằng đó là các lớp hoàn toàn khác nhau, không có điểm chung nào ngoại trừ việc chúng được tạo từ cùng một mẫu lớp.

Areakhông phải là một lớp nên bạn không thể lấy lớp Rectangletừ nó. Bạn chỉ có thể lấy một lớp từ một lớp khác (hoặc một vài trong số chúng). Vì Area<int>là một lớp, chẳng hạn, bạn có thể lấy Rectangletừ nó:

class Rectangle:
  public Area<int>
{
  // ...
};

Area<int>Area<char>là các lớp khác nhau, bạn thậm chí có thể lấy từ cả hai cùng một lúc (tuy nhiên khi truy cập các thành viên của chúng, bạn sẽ phải đối mặt với sự mơ hồ):

class Rectangle:
  public Area<int>,
  public Area<char>
{
  // ...
};

Tuy nhiên, bạn phải chỉ định được phân loại để lấy từ khi bạn xác định Rectangle. Điều này đúng cho dù các lớp đó có được tạo từ một khuôn mẫu hay không. Hai đối tượng của cùng một lớp đơn giản không thể có phân cấp kế thừa khác nhau.

Những gì bạn có thể làm là tạo Rectanglemột khuôn mẫu. Nếu bạn viết

template<typename T> class Rectangle:
  public Area<T>
{
  // ...
};

Bạn có một mẫu Rectanglemà từ đó bạn có thể lấy một lớp Rectangle<int>dẫn xuất từ ​​đó Area<int>và một lớp khác Rectangle<char>dẫn xuất từ ​​đó Area<char>.

Có thể bạn muốn có một kiểu duy nhất Rectangleđể bạn có thể chuyển tất cả các loại Rectanglecho cùng một hàm (bản thân nó không cần biết Kiểu vùng). Vì các Rectangle<T>lớp được tạo ra bằng cách khởi tạo mẫu Rectanglelà độc lập về mặt hình thức với nhau, nên nó không hoạt động theo cách đó. Tuy nhiên, bạn có thể sử dụng đa kế thừa tại đây:

class Rectangle // not inheriting from any Area type
{
  // Area independent interface
};

template<typename T> class SpecificRectangle:
  public Rectangle,
  public Area<T>
{
  // Area dependent stuff
};

void foo(Rectangle&); // A function which works with generic rectangles

int main()
{
  SpecificRectangle<int> intrect;
  foo(intrect);

  SpecificRectangle<char> charrect;
  foo(charrect);
}

Nếu điều quan trọng là giá trị chung của bạn Rectanglecó nguồn gốc từ chung, Areabạn cũng có thể thực hiện thủ thuật tương tự Area:

class Area
{
  // generic Area interface
};

class Rectangle:
  public virtual Area // virtual because of "diamond inheritance"
{
  // generic rectangle interface
};

template<typename T> class SpecificArea:
  public virtual Area
{
  // specific implementation of Area for type T
};

template<typename T> class SpecificRectangle:
  public Rectangle, // maybe this should be virtual as well, in case the hierarchy is extended later
  public SpecificArea<T> // no virtual inheritance needed here
{
  // specific implementation of Rectangle for type T
};

Lưu ý: Tham số kiểu mẫu là một phần của lớp đối tượng khởi tạo mẫu lớp đó. Nói cách khác, nó không phải là một thành phần của lớp, nó là một phần của kiểu.
Nikos

21

Bạn chỉ đang cố gắng bắt nguồn từ Area<int>? Trong trường hợp bạn làm điều này:

class Rectangle : public Area<int>
{
    // ...
};

CHỈNH SỬA: Sau khi làm rõ, có vẻ như bạn cũng đang thực sự cố gắng tạo Rectanglemột mẫu, trong trường hợp đó, cách sau sẽ hoạt động:

template <typename T>
class Rectangle : public Area<T>
{
    // ...
};


8

Đặt Rectangle làm mẫu và chuyển tên kiểu cho Khu vực:

template <typename T>
class Rectangle : public Area<T>
{

};

6

Rectanglesẽ phải là một khuôn mẫu, nếu không nó chỉ là một kiểu . Nó không thể là một phi mẫu trong khi cơ sở của nó là một cách kỳ diệu. (Cơ sở của nó có thể là một bản khởi tạo mẫu , mặc dù bạn dường như muốn duy trì chức năng của cơ sở như một khuôn mẫu .)


3
#include<iostream>

using namespace std;

template<class t> 
class base {
protected:
    t a;
public:
    base(t aa){
        a = aa;
        cout<<"base "<<a<<endl;
    }
};

template <class t> 
class derived: public base<t>{
    public:
        derived(t a): base<t>(a) {
        }
        //Here is the method in derived class 
    void sampleMethod() {
        cout<<"In sample Method"<<endl;
    }
};

int main() {
    derived<int> q(1);
    // calling the methods
    q.sampleMethod();
}

điều gì sẽ xảy ra nếu bạn có các phương thức trong lớp dẫn xuất? Bạn định nghĩa chúng như thế nào? Và hầu hết nhập, ít nhất là đối với tôi, làm thế nào để bạn gọi / sử dụng các phương thức đó trong hàm main () của mình?
Pototo

6
Đầu tiên, đây là một câu hỏi thực sự cũ đã có câu trả lời tuyệt vời. Thứ hai, vui lòng tránh các câu trả lời (và câu hỏi) chỉ là mã. Nó cũng hữu ích để bao gồm các giải thích chi tiết.
marcman
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.