2008-10-25 8 views
6

Đây là vấn đề của tôi: Tôi có một phương thức ảo được định nghĩa trong tệp .h mà tôi muốn gọi trong một lớp kế thừa từ lớp cơ sở. Đáng buồn thay, phương thức trong lớp dẫn xuất không được gọi. Có cách nào tốt hơn để thực hiện những gì tôi đang cố gắng làm không?Thừa kế trong C++

#ifndef ofxBASE_SND_OBJ 
#define ofxBASE_SND_OBJ 

#include "ofConstants.h" 

class ofxBaseSndObj { 

public: 

    virtual string getType(){} 

    string key; 

}; 

#endif 

Đây là lớp buzz tôi

#ifndef OFXSO_BUZZ 
#define OFXSO_BUZZ 

#include "ofxBaseSndObj.h" 

class ofxSOBuzz : public ofxBaseSndObj 
{ 
public: 
    string getType(); 
}; 

#endif 

ofxSOBuzz.cpp

string ofxSOBuzz::getType() 
{ 
    string s = string("ofxSOBuzz"); 
    printf(" ********* returning string type %s", s.c_str()); // doesn't get called! 
    return s; 
} 

Sau đó, trong một lớp học tôi cố gắng gọi nó theo cách này:

string ofxSndObj::createFilter(ofxBaseSndObj obj) 
{ 
    string str = obj.getType(); 
    if(str.compare("ofxSOBuzz") == 0) 
    { 
     printf(" all is well "); 
    } 
} 

Trong phương pháp này ở trên tôi cần để có thể vượt qua trong một m bất kỳ loại đối tượng nào mà tất cả đều mở rộng đối tượng ofxBaseSndObj. Mọi gợi ý hay gợi ý sẽ được đánh giá cao. Cảm ơn!

+1

Theo như tôi biết, tiêu đề của bạn (nơi bạn xác định ofxBaseSndObj) sẽ không biên dịch vì bạn có hàm có loại trả về không trống mà không có câu lệnh trả về. Làm thế nào bạn quản lý để chạy này? – Arkadiy

+0

Về thời gian để chấp nhận câu trả lời đúng !! Nhấp vào một trong các dấu kiểm bên cạnh câu trả lời mà bạn cho là trả lời câu hỏi tốt nhất. –

Trả lời

25

Thay đổi dòng này:

string ofxSndObj::createFilter(ofxBaseSndObj obj) 

để

string ofxSndObj::createFilter(ofxBaseSndObj& obj) 

gì bạn đang làm là đi ngang qua giá trị (thông qua một bản sao).

Điều này có nghĩa là bạn đang sao chép đối tượng vào chức năng. Bởi vì hàm không biết loại bạn đang thực sự truyền nó chỉ vượt qua kiểu được xác định trong khai báo hàm và do đó nó tạo một bản sao của lớp cơ sở (điều này được biết là vấn đề slicing).

Giải pháp là chuyển qua tham chiếu.

Nếu bạn không muốn chức năng sửa đổi đối tượng (có thể đó là lý do bạn chuyển giá trị sao cho nó không thể thay đổi bản gốc), sau đó vượt qua tham chiếu const.

class ofxBaseSndObj 
{ 
    public: 
     virtual string getType() const; 
     // If the method does not change the object mark it const 

     string key; 

}; 

string ofxSndObj::createFilter(ofxBaseSndObj const& obj) 
{ 
    // allowed to call this if getType() is a const 
    string str = obj.getType(); 

    if(str.compare("ofxSOBuzz") == 0) 
    { 
     printf(" all is well "); 
    } 
} 
10

Bạn cần chuyển thể hiện để tạoFilter dưới dạng con trỏ (hoặc tham chiếu) cho đối tượng. Bạn là passing by value và điều này làm cho trình biên dịch sao chép đối tượng dẫn xuất bạn sử dụng làm đối số vào một cá thể của lớp cơ sở. Khi nó làm điều này bạn mất một thực tế rằng nó ban đầu là một loại có nguồn gốc.

Khi viết mã của bạn không nên thực sự biên dịch kể từ khi khai báo ofxBaseSndObj :: getType không trả về bất kỳ thứ gì. Ý của bạn có phải là một phương thức trừu tượng hoặc trả về một chuỗi rỗng không?

Nếu bạn làm cho nó thành một phương thức trừu tượng thì trình biên dịch sẽ phàn nàn về việc cố gắng khởi tạo một lớp trừu tượng trong phương thức ofxSndObj :: createFilter của bạn.

+0

Vâng, bạn nói đúng, kinh khủng. Nó không nên biên dịch ... kỳ quặc. Tôi đang trên gcc4.2, tôi tự hỏi nếu một người nào khác đã chạy vào điều này? –

2

Sự cố này được gọi là "slicing" bằng C++.

2

Làm cho hàm tạo bản sao và toán tử = private là một cách hiệu quả để ngăn chặn lỗi này xảy ra lần nữa.

Ví dụ:

class ofxBaseSndObj { 
public: 
    virtual string getType(){} 
    string key; 

private: 
    ofxBaseSndObj(const ofxBaseSndObj& rhs); 
    ofxBaseSndObj& operator=(const ofxBaseSndObj& rhs); 
}; 

Nếu không có lý do chính đáng khác mà bạn nên sử dụng C++ 's xây dựng trong RTTI.Sau đó bạn có thể sử dụng toán tử typeid. Xem tài liệu trình biên dịch của bạn để bật tính năng này nếu nó không được bật theo mặc định.

+0

Vâng, tôi sẽ thiết kế lại để chỉ sử dụng con trỏ và RTTI thay thế. Tôi muốn tránh đi qua tham chiếu (câu chuyện dài, API có cơ sở người dùng lẻ) nhưng tôi nghĩ đó là cách duy nhất nó hoạt động và đó là cách có ý nghĩa nhất. Cảm ơn! –

-1

Bạn có thể sử dụng dynamic_cast hoặc type_id

1

Một số khác đã giải quyết được vấn đề về cắt. Sau đó, bạn hãy hỏi Ok, hãy để tôi nói, tôi biết tôi cần phải làm một cái gì đó để xác định loại cơ sở, nhưng có cái gì đó thanh lịch hơn làm một tra cứu enum để xác định loại đối tượng thừa kế?

Truy vấn và chuyển đổi loại đối tượng là thiết kế kém mà bỏ qua điểm của phương pháp tiếp cận OO.

Thay vì

string ofxSndObj::createFilter(ofxBaseSndObj& obj) 
{ 
    string str = obj.getType(); 
    if(str.compare("ofxSOBuzz") == 0) 
    { 
     // do ofxSOBuzz - specific thing 
    } 
    else if(str.compare("some other derived class") == 0) 
    { 
     // do stuff for other derived classes 
    } 
     // etc... 
} 

làm cho hành vi thú vị chức năng ảo:

class ofxBaseSndObj { 

public: 
    // get rid of getType() 
    virtual void HelpCreateFilter() = 0; 
}; 


string ofxSndObj::createFilter(ofxBaseSndObj& obj) 
{ 
    // Let the derived class do it's own specialized work. 
    // This function doesn't need to know what it is. 
    obj.HelpCreateFilter(); 
    // rest of filter creation 
} 

Tại sao điều này tốt hơn so với phiên bản gốc? Bởi vì ofxSndObj::createFilter không cần phải sửa đổi nếu các lớp dẫn xuất trong tương lai của ofxBaseSndObj được thêm vào hệ thống. Phiên bản của bạn cần mở rộng cho mỗi lớp dẫn xuất mới. Nếu điều này không rõ ràng, hãy thử đăng thêm một đoạn mã nữa - tôi không thể nói từ mã của bạn hoặc tên lớp mà những chức năng này được yêu cầu làm.

+0

Anh ấy đã biến mất - và đưa ra câu hỏi của anh ấy với anh ấy. – fizzer