2008-10-23 33 views
12

Tôi muốn có một tầng lớp phụ huynh ảo tinh khiết để gọi một thực hiện con của một chức năng như vậy:C++ lớp Chánh gọi một hàm ảo con

class parent 
{ 
    public: 
    void Read() { //read stuff } 
    virtual void Process() = 0; 
    parent() 
    { 
     Read(); 
     Process(); 
    } 
} 
class child : public parent 
{ 
    public: 
    virtual void Process() { //process stuff } 
    child() : parent() { } 
} 

int main() 
{ 
    child c; 
} 

này nên làm việc, nhưng tôi nhận được một lỗi bỏ liên kết:/Đây là sử dụng VC++ 2k3

Hoặc nó không hoạt động, tôi có sai không?

Trả lời

19

Tiêu đề của bài viết sau nói lên tất cả: Never Call Virtual Functions during Construction or Destruction.

+2

Đúng, nhưng hơi ngắn. Ít nhất bạn nên sao chép kết luận: "Những điều cần nhớ: Đừng gọi các chức năng ảo trong quá trình xây dựng hoặc hủy diệt, bởi vì các cuộc gọi như vậy sẽ không bao giờ đi đến một lớp được dẫn xuất nhiều hơn so với các hàm tạo hiện tại hay hàm hủy." – paercebal

+0

Tuy nhiên, tôi thực sự nghĩ rằng bạn nên suy nghĩ hai lần trước khi trộn trong chức năng của thừa kế. IMHO, câu hỏi này cho thấy một dòng chảy thiết kế dưới bề mặt. – xtofl

2

Sẽ hoạt động nói chung, chứ không phải cho các cuộc gọi bên trong hàm tạo của lớp cơ sở ảo thuần túy. Tại thời điểm lớp cơ sở được xây dựng, ghi đè lớp phụ không tồn tại, vì vậy bạn không thể gọi nó. Miễn là bạn gọi nó một khi toàn bộ đối tượng được xây dựng, nó sẽ hoạt động.

2

Đó là vì cuộc gọi của bạn ở trong hàm tạo. Lớp dẫn xuất sẽ không hợp lệ cho đến khi hàm tạo đã hoàn thành, do đó trình biên dịch của bạn là đúng trong việc dinging bạn cho việc này.

Có hai giải pháp:

  1. Làm cho cuộc gọi đến Process() trong constructor lớp được thừa kế của
  2. xác định một cơ quan chức năng trống cho quá trình như trong ví dụ sau:
class parent 
{ 
    public: 
    void Read() { //read stuff } 
    virtual void Process() { } 
    parent() 
    { 
     Read(); 
     Process(); 
    } 
} 
+0

Đây là nguy hiểm, xác định một trống chức năng cơ thể trong phụ huynh và gọi nó trong constructor của nó sẽ dẫn đến chỉ là một phần cha của Process() (tức là không có gì) đang được thực hiện. Có thể anh ta muốn cuộc gọi được giải quyết như một chức năng ảo, điều đó là không thể trong nhà xây dựng – Pieter

+0

Thật vậy: chức năng trống sẽ được gọi. Đó không phải là giải pháp khả thi. – xtofl

4

Cách khác, tạo một phương thức nhà máy để tạo đối tượng và đặt các hàm tạo riêng tư, phương thức nhà máy có thể khởi tạo đối tượng sau khi xây dựng.

0

Bạn cần phải bọc ở bên trong một đối tượng mà gọi phương thức ảo sau khi đối tượng được hoàn toàn xây dựng:

class parent 
{ 
    public: 
    void Read() { /*read stuff*/ } 
    virtual void Process() = 0; 
    parent() 
    { 
     Read(); 
    } 
}; 

class child: public parent 
{ 
    public: 
    virtual void Process() { /*process stuff*/ } 
    child() : parent() { } 
}; 

template<typename T> 
class Processor 
{ 
    public: 
     Processor() 
      :processorObj() // Pass on any args here 
     { 
      processorObj.Process(); 
     } 
    private: 
     T processorObj; 

}; 




int main() 
{ 
    Processor<child> c; 
} 
2

Với một bước nữa bạn chỉ có thể giới thiệu một số loại của một hàm như

class parent 
{ 
    public: 
     void initialize() { 
      read(); 
      process(); 
     } 
} 
0

hời hợt vấn đề là bạn gọi một chức năng ảo chưa được biết (Các đối tượng được xây dựng từ cha mẹ sang con, do đó cũng là vtables). Trình biên dịch của bạn cảnh báo bạn về điều đó.

Bài báo thiết yếu, theo như tôi thấy, là bạn cố gắng sử dụng lại chức năng của kế thừa. Đây gần như luôn luôn là một ý tưởng tồi. Một vấn đề thiết kế, có thể nói :)

Về cơ bản, bạn hãy thử instantiating một phương pháp mô hình mẫu, để tách các từ khi: trước hết hãy đọc một số dữ liệu (một cách nào đó), sau đó xử lý nó (trong một số cách).

Điều này có thể sẽ hoạt động tốt hơn với tập hợp: cung cấp chức năng Xử lý cho phương thức Mẫu để được gọi vào đúng thời điểm. Có lẽ bạn thậm chí có thể làm tương tự cho chức năng Đọc.

Việc tổng hợp có thể được thực hiện theo hai cách:

  1. Sử dụng chức năng ảo (ví dụ:Runtime Binding)
  2. Sử dụng mẫu (tức là thời gian biên dịch Binding)

Ví dụ 1: runtime ràng buộc

class Data {}; 
class IReader { public: virtual Data read()   = 0; }; 
class IProcessor { public: virtual void process(Data& d) = 0; }; 

class ReadNProcess { 
public: 
    ReadNProcess(IReader& reader, IProcessor processor){ 
     processor.process(reader.read()); 
    } 
}; 

Ví dụ 2: compiletime ràng buộc

template< typename Reader, typename Writer > // definitely could use concepts here :) 
class ReadNProcess { 
public: 
    ReadNProcess(Reader& r, Processor& p) { 
     p.process(r.read()); 
    } 
}; 
Các vấn đề liên quan