2010-06-11 1257 views
6

Chúc mừng tất cả,C++ Nhiều kế thừa với giao diện?

Tôi đến từ nền Java và tôi gặp khó khăn với đa kế thừa.

Tôi có một giao diện được gọi là IView có phương thức init(). Tôi muốn lấy một lớp mới có tên là PlaneViewer triển khai giao diện trên và mở rộng một lớp khác. (QWidget).

thực hiện của tôi là như sau:

IViwer.h (chỉ tập tin Header, không có tập tin CPP):

#ifndef IVIEWER_H_ 
#define IVIEWER_H_ 

class IViewer 
{ 
public: 
    //IViewer(); 
    ///virtual 
    //~IViewer(); 
    virtual void init()=0; 
}; 

#endif /* IVIEWER_H_ */ 

lớp được thừa kế của tôi.

PlaneViewer.h

#ifndef PLANEVIEWER_H 
#define PLANEVIEWER_H 

#include <QtGui/QWidget> 
#include "ui_planeviewer.h" 
#include "IViewer.h" 
class PlaneViewer : public QWidget , public IViewer 
{ 
    Q_OBJECT 

public: 
    PlaneViewer(QWidget *parent = 0); 
    ~PlaneViewer(); 
    void init(); //do I have to define here also ? 

private: 
    Ui::PlaneViewerClass ui; 
}; 

#endif // PLANEVIEWER_H 

PlaneViewer.cpp

#include "planeviewer.h" 

PlaneViewer::PlaneViewer(QWidget *parent) 
    : QWidget(parent) 
{ 
    ui.setupUi(this); 
} 

PlaneViewer::~PlaneViewer() 
{ 

} 

void PlaneViewer::init(){ 

} 

Câu hỏi của tôi là:

  1. Có cần phải khai báo phương pháp init() trong giao diện PlaneViewer cũng bởi vì nó đã được định nghĩa rồi n IView?

2.I không thể complie trên mã, đưa ra báo lỗi:

PlaneViewer] + 0x28): tài liệu tham khảo không xác định cho `typeinfo cho IViewer' collect2: ld trở về 1 trạng thái thoát

Tôi có để thực hiện cho IView trong tệp CPP (vì tất cả những gì tôi muốn là một giao diện, không phải là triển khai)?

+0

Tôi có thể hỏi về thiết kế của bạn không? Tại sao bạn cần phải liên kết với nhau QWidget và IViewer trong cùng một hệ thống phân cấp thừa kế? Bạn đang cố gắng giải quyết vấn đề gì với nhiều thừa kế? Tôi hỏi vì đa thừa kế là hữu ích trong một vài trường hợp hiếm gặp nhưng thường các sự cố được giải quyết tốt hơn theo cách khác. –

+0

Trong ứng dụng của tôi có một số loại người xem có cùng dữ liệu. (Dữ liệu voxel 3D) .Eg: người xem 2D (máy bay XY, Máy bay YZ, Máy bay ZX) và trình xem 3D. Và trong tương lai sẽ có thêm một số người xem. QWiget là sử dụng bản vẽ và hiển thị Dữ liệu. IView là một lớp/giao diện trừu tượng để khai báo các phương thức và dữ liệu lệnh cho tất cả các kiểu người xem. –

Trả lời

3

Có cần phải khai báo phương pháp init() trong PlaneViewer giao diện cũng có, bởi vì nó đã được xác định trong IView?

Bạn không phải khai báo init() trong PlaneViewer, nhưng nếu bạn không PlaneViewer sẽ là lớp trừu tượng, nghĩa là bạn không thể khởi tạo lớp đó.

Nếu bạn muốn hỏi bạn có phải có 'void init();' không trong tệp tiêu đề cho PlaneViewer và trong tệp .cpp. Câu trả lời là có.

tôi không thể complie trên mã, đưa ra báo lỗi: PlaneViewer] + 0x28): không xác định tài liệu tham khảo cho `typeinfo cho IViewer' collect2: ld trở về 1 exit status

Tôi nghĩ rằng một trong hai bạn không xây dựng cùng một mã hoặc lệnh biên dịch của bạn không chính xác.

Tôi đã loại bỏ công cụ QT và có thể xây dựng mã của bạn tốt với g ++.

Lỗi có nghĩa là lớp IViewer không được tìm thấy bởi trình liên kết.

Tôi nhận được lỗi đó nếu tôi xóa phần '= 0' làm cho 'IViewer :: init()' là một hàm ảo thuần túy. Bạn cũng có thể nhận được lỗi đó nếu bạn uncommented constructor và/hoặc destructor trong IViewer.

Tôi có phải triển khai IView trong tệp CPP không?

No. C++ không quan tâm nếu tệp đó nằm trong tệp .cpp hoặc tệp .h. Không giống như Java, bộ tiền xử lý C/C++ đầu tiên giải quyết tất cả các gói và tạo ra một tệp chứa tất cả mã. Sau đó nó chuyển nó tới trình biên dịch C/C++. Bạn thực sự có thể bao gồm một .cpp nếu bạn muốn. Không phải là một ý tưởng tốt mặc dù.

7

Một cách hay để suy nghĩ về các lớp giao diện là chúng xác định các phương thức mà các lớp bắt nguồn phải thực hiện.

Có cần phải khai báo phương pháp init() trong giao diện PlaneViewer cũng có, bởi vì nó đã được xác định trong IView?

Câu trả lời nhanh là có, bạn phải triển khai phương thức init trong IViewer vì trong lớp cơ sở, phương thức được khai báo là thuần ảo. Điều này có nghĩa rằng bất kỳ lớp dẫn xuất nào PHẢI cung cấp việc thực hiện riêng của phương thức đó vì không có phương thức lớp cơ sở nào được triển khai.

2.Tôi không thể complie trên mã, đưa ra báo lỗi:

PlaneViewer] + 0x28): không xác định tài liệu tham khảo cho `typeinfo cho IViewer' collect2: ld trở về 1 exit status

Đây là một g ++ biên dịch báo lỗi cho biết (như đã nêu ở trên) mà bạn có một lớp dẫn xuất từ ​​một cơ sở có một hàm ảo thuần túy và lớp dẫn xuất không thực hiện phương thức ảo thuần túy, vì nó phải là. Ồ, cũng cần lưu ý rằng bạn không gặp vấn đề với đa thừa kế, vấn đề vẫn tồn tại nếu chỉ có IViewerPlaneViewer được tham gia.

2

Có, bạn cần phải khai báo lại virtual void init() trong phân lớp và triển khai nó, vì IViewer tuyên bố hàm sẽ là thuần ảo.

Xem another question để giải thích về lỗi của bạn. Đó là do khai báo một hàm ảo (không thuần túy) và không định nghĩa nó. Nó không rõ ràng từ mã bạn đăng, vì vậy tôi nghi ngờ bạn có thể có tập tin đối tượng cũ mà không được xây dựng lại (bạn đã nhận xét ra IViewer constructor và destructor ảo).

Lưu ý bổ sung, you should provide virtual destructors with empty body for your interfaces.

4

Có, bạn phải khai báo init trong số PlaneViewer của mình. Nếu bạn không làm như vậy, thì init sẽ không tồn tại trong PlaneViewerPlaneViewer sẽ vẫn được coi là trừu tượng (vì không có triển khai init).

Bạn cần phải xác định các thân trống cho (destructor) ảo của bạn trong IViewer. "Giao diện" trong C++ không thực sự là giao diện, nó chỉ theo quy ước mà bạn tạo một lớp với tất cả các phương thức thuần ảo và không có trường nào: tuy nhiên, chúng vẫn chỉ là các lớp "thông thường" từ quan điểm của trình biên dịch, vì vậy bạn vẫn cần phải cung cấp một thực hiện các destructor.

class IViewer 
{ 
public: 
    IViewer() { } 
    virtual ~IViewer() { } 

    virtual void init() = 0; 
}; 
3

Vấn đề typeinfo là do không có triển khai trình phá hủy cho lớp IViewer. Thông thường các trình biên dịch sẽ tạo ra các cấu trúc dữ liệu nội bộ (ví dụ: "typeinfo") cùng với trình phá hủy ảo.

Bạn cần phải biên dịch và liên kết một tập tin có chứa:

#include "iviewer.h" 

IViewer::~IViewer() { } 

Đó là thực tế tốt để có một destructor ảo bởi vì điều này sẽ cho trình biên dịch một đơn vị biên soạn để sử dụng thông tin RTTI, và nó cũng cho phép xóa toán tử hoạt động chính xác khi được gọi trên một con trỏ lớp cơ sở.

Một số khác trả lời câu hỏi trên phương thức init(), nhưng tóm lại: Nếu bạn định triển khai nó trong PlaneViewer, bạn cần khai báo nó.

2

tôi đã thực hiện công việc quan trọng trong cả hai ngôn ngữ và có một mô hình cắt cookie bạn thường có thể làm theo để biến một giao diện Java thành một C++ giao diện:

// Bắt đầu với Java Interface

interface Numeric { 
    public int  toInteger(); 
    public double toDouble(); 
}; 

C++ đặt trước Java và không bận tâm việc xác định từ khóa "giao diện" đặc biệt cho các lớp ảo thuần túy. Vì vậy, bạn có hiệu quả phải làm một số công việc mà các trình biên dịch Java không tự động:

// C tương đương ++ lớp

class Numeric { 
private: 
    Numeric(const Numeric&); 
    Numeric& operator=(const Numeric&); 
public: 
    Numeric() {} 
    virtual ~Numeric() {} 

    virtual int toInteger() = 0; 
    virtual double toDouble() = 0; 
}; 

Một nguyên tắc tốt để làm theo trong C++ là, bất cứ khi nào bạn kế thừa từ một lớp cơ sở với tinh khiết các phương thức ảo, khai báo lại chúng trong lớp dẫn xuất ngay cả khi bạn để chúng như là các virtual virtuals. Nó không làm tổn thương hiệu suất và nó cho phép mọi người biết rằng đối tượng chỉ là một phần thực hiện.