2012-09-17 50 views
0

我有一個基於網絡(TCP-IP)的自定義IPC系統。boost :: shared_ptr修改後的工廠方法

考慮代碼(和解釋波紋管):

#include "boost/shared_ptr.hpp" 
#include <string> 

using namespace std; 

class TCommand { 
public: 
    typedef boost::shared_ptr<TCommand> Ptr; 

    TCommand() { 
     cout << " Creating TCommand..." << endl; 
    } 

    virtual ~TCommand() { 
     cout << " Destroying TCommand..." << endl; 
    } 

    static TCommand * factory(int classID); 

    virtual void parse(const char *data, int dataSize) = 0; 
    virtual void print() = 0; 
    virtual std::string getType() = 0; 

}; 


class TPingCommand : public TCommand { 
public: 
    static const int classID = 1; 
    int value; 

    TPingCommand() : TCommand() { 
     cout << " Creating TPingCommand..." << endl; 
    } 

    virtual ~TPingCommand() { 
     cout << " Destroying TPingCommand..." << endl; 
    } 

    virtual void parse(const char *data, int dataSize) { 
    if (dataSize < 4) throw 1; 

    this->value = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]; 
    } 

    virtual void print() { 
     cout << " TPingCommand:" << endl; 
     cout << " value = " << dec << this->value << " (0x" << hex << this->value << ")" << endl; 
    } 

    virtual std::string getType() { 
     return "TPingCommand"; 
    } 
}; 

class TOtherCommand : public TCommand { 
public: 
    static const int classID = 2; 
    int value; 
    char value2; 
    short int value3; 

    TOtherCommand() : TCommand() { 
     cout << " Creating TOtherCommand..." << endl; 
    } 

    virtual ~TOtherCommand() { 
     cout << " Destroying TOtherCommand..." << endl; 
    } 

    virtual void parse(const char *data, int dataSize) { 
    if (dataSize < 7) throw 1; 

    this->value = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]; 
    this->value2 = data[4]; 
    this->value3 = data[5] << 8 | data[6]; 
    } 

    virtual void print() { 
     cout << " TOtherCommand:" << endl; 
     cout << " value = " << dec << this->value << " (0x" << hex << this->value << ")" << endl; 
     cout << " value2 = " << dec << this->value2 << " (0x" << hex << (int)this->value2 << ")" << endl; 
     cout << " value3 = " << dec << this->value3 << " (0x" << hex << this->value3 << ")" << endl; 
    } 

    virtual std::string getType() { 
     return "TOtherCommand"; 
    } 
}; 


TCommand * TCommand::factory(int classID) { 
    cout << " Factory for classID = " << dec << classID << " (0x" << hex << classID << ")" << endl; 
    switch (classID) { 
    case TPingCommand::classID: return new TPingCommand(); break; 
    case TOtherCommand::classID: return new TOtherCommand(); break; 
    default: throw 1; 
    } 
    } 


TCommand::Ptr receiveFromNetwork(int test, TCommand::Ptr knownCommand) 
{ 
    // Receive command header from network. 
    // int classID is the command class internal ID. 
    // int dataSize is the command's body size in bytes. 
    // For instance: 
    // int classId = 2; 
    // int datasize = 7; 

    int classId = 1; 
    int dataSize = 4; 
    char data[10]; 

    if (test == 0) { 
     cout << " Using test data 0..." << endl; 
     classId = 1; 
     dataSize = 4; 
     data[0] = 0x01; data[1] = 0x02; data[2] = 0x03; data[3] = 0x04; 
    } else if (test == 1) { 
     cout << " Using test data 1..." << endl; 
     classId = 2; 
     dataSize = 7; 
     data[0] = 0x11; data[1] = 0x12; data[2] = 0x13; data[3] = 0x14; data[4] = 0x41; data[5] = 0x16; data[6] = 0x17; 
    } 

    TCommand::Ptr cmd; 
    if (knownCommand == 0) { 
     cout << " No command provided." << endl; 
     cmd.reset(TCommand::factory(classId)); 
     cout << " Command created from factory: " << cmd->getType() << endl; 
    } else { 
     cmd = knownCommand; 
     cout << " Command provided: " << cmd->getType() << endl; 
    } 

    cout << " Parsing data..." << endl; 
    cmd->parse(data, dataSize); 

    // The command was identified as TOtherCommand (classID = 2). 
    // The factory returned a TOtherCommand instance. 
    // The TOtherCommand's parse method will check the dataSize is suitable (7 bytes are necessary). 
    // The parse method will unserialize data to their fields. 
    // This way, the fields would be: 
    // data = 0x11121314; 
    // data2 = 0x42; // 'A' as char. 
    // data3 = 0x1213; 

    return cmd; 
} 

void caller() { 
    // Case 1 (ok): 
    // I know I'm going to receive a TPingCommand. 
    cout << "Test case 1:" << endl; 
    TCommand::Ptr known(new TPingCommand()); 
    TCommand::Ptr cmd1 = receiveFromNetwork(0, known); 
    cmd1->print(); 

    // Case 2 (problems): 
    cout << "Test case 2:" << endl; 
    TCommand::Ptr dummy; 
    TCommand::Ptr cmd2 = receiveFromNetwork(1, dummy); 
    cmd2->print(); 

    cout << "Teardown..." << endl; 
} 

int main() { 
    caller(); 
} 

的receiveFromNetwork是排序,用來從網絡接收的命令,然而,在少數情況下的改性工廠方法,我知道通過先驗我將接收哪種類型的命令,所以我創建它的實例並傳遞給函數(如knownCommand)。命令類是從TCommand類派生的。已知命令由函數返回(它不是必需的,因爲您將該函數作爲參數傳遞,但對其他情況有用)。

所有其他情況下,從網絡收到的前幾個字節描述命令classID,我用它在該函數內創建合適的TCommand實例。然後該命令從網絡數據中解析出來並在函數結束時返回。 knownCommand只是一個虛擬的TCommand實例。

當我通過knownCommand時,效果很好。 當我傳遞一個虛擬命令作爲參數時,它崩潰(據我所知雙倍釋放)。

我想過使用TCommand引用進行knownCommand,但是我不能這樣做,因爲我必須返回一個共享指針,並且它會導致相同的原始指針由兩個不同的共享指針實例管理一個來自調用方法,另一個來自receiveFromNetwork方法)。

任何人都有如何解決這個問題的好主意?

這裏是有問題的情況下的部分Valgrind的輸出:

==31859== Thread 2: 
==31859== Invalid read of size 4 
==31859== at 0x805D7B0: boost::detail::sp_counted_impl_p<TVideoGetSourceSizeCommand>::dispose() (checked_delete.hpp:34) 
==31859== by 0x407FF42: Server::receiveFromNetwork() (sp_counted_base_gcc_x86.hpp:145) 
==31859== by 0x40800AF: Server::serverThread(void*) (Server.cpp:107) 
==31859== by 0x434496D: start_thread (pthread_create.c:300) 
==31859== by 0x42B398D: clone (clone.S:130) 

非常感謝。

+0

你是什麼意思的虛擬命令?它支持get()嗎? –

+0

虛擬命令只是一個持有空指針的shared_ptr(我不確定它是否允許)。我在調用者函數中將其創建爲虛擬。看一下這個。你認爲這是錯的嗎? – Marcus

+0

你可以嘗試在調試器中運行它,然後給我們發生確切的錯誤信息?你也可以嘗試在valgrind中運行它。 – codeling

回答

0

發現問題。

在代碼的調用部分,有一個shared_ptr被錯誤地「複製」。 產生的錯誤是這樣的:

boost::shared_ptr<DerivedType> ptr1(new DerivedType(...)); 
boost::shared_ptr<BaseType> ptr2(ptr1.get()); 

當然這是要引起復制的自由和程序崩潰。

該代碼並不那麼愚蠢,但最終是簡化的錯誤情況。 儘管這是一個微不足道的錯誤。

感謝所有的幫助傢伙。