簡體   English   中英

正確使用std智能指針確保ptr安全

[英]Correct way to use std smart pointers to ensure ptr safety

這是使用std智能指針確保ptr安全的正確方法

這個例子可能不是最好的,但我試圖模仿一些真正的代碼。 我遇到的問題是在實際代碼中,通信器指針是一個原始指針,可以在任何時刻解除分配 - 導致使用指針崩潰。

所以我決定查看std :: shared_ptr和std :: weak_ptr以了解它應該如何設計現在我們有C ++ 11。 我在發送代碼中使用weak_ptr來檢查ptr是否仍然有效,然后才會取消引用ptr。 這段代碼是正確的方法嗎? 有什么改進?

#include <memory>
#include <iostream>
#include <string>

class communicator
{
public:
    communicator(const char* name, int comport, int speed) : name_(name), comport_(comport), speed_(speed) { }

    void send(const std::string& s) {
        std::cout << "sending " << s << " using " << name_ << " at " << speed_ << " rate and using com port " << comport_ << '\n';
    }

private:
    const char* name_;
    int comport_;
    int speed_;
};

class sender
{
public:
    sender() {}

    void set_communicator(std::weak_ptr<communicator> comms) {
        comms_ = comms;
    }

    void send(const std::string& s)
    {
        if (auto sh = comms_.lock())
            sh->send(s);
        else
            std::cout << "Attempting to send: " << s << " but ptr no longer exists\n";
    }

private:
    std::weak_ptr<communicator> comms_;
};

int main() {

    sender mysender;

    {
        // create comms object
        std::shared_ptr<communicator> comms(new communicator("myname", 3, 9600));

        mysender.set_communicator(comms);

        mysender.send("Hi guys!");

    }  // comms object gets deleted here

    mysender.send("Hi guys after ptr delete!");
}

輸出:

sending Hi guys! using myname at 9600 rate and using com port 3
Attempting to send: Hi guys after ptr delete! but ptr no longer exists

可以在任何時刻解除分配的指針 - 導致使用指針時崩潰

這是引入weak_ptr的理由背后的症狀; 因此,我認為你的基於weak_ptr的方法是正確的。

然而,我發現有爭議的是與此相關

sender() : comms_() {}

void set_communicator(std::weak_ptr<communicator> comms) {
    comms_ = comms;
}

排序的最的的兩階段建設sender的內部資產comms_你沒有內部資產的狀態重置后施工狀態,一旦鎖()中失敗

void send(const std::string& s)

但這本身並不是“錯誤的”; 它只是可以考慮用於全尺寸應用程序的東西。

另一件事是當lock()失敗時你不會throw (或者拋出shared_ptr(weak_ptr) ctor(#11) ),只是if-else處理它。 我不知道您的完整應用程序的要求,但根據您組裝的提取,基於異常的錯誤處理將改善設計imo。

例如:

#include <memory>
#include <stdexcept>
#include <iostream>
#include <string>

class communicator
{
public:
    communicator(const char* name, int comport, int speed) 
        : name_(name), comport_(comport), speed_(speed) { }

    void send(const std::string& s) {
        std::cout << "sending " << s << " using " << name_ << " at " 
                  << speed_ << " rate and using com port " << comport_ 
                  << '\n';
    }

private:
    const char* name_;
    int comport_;
    int speed_;
};

class sender
{
public:
    struct invalid_communicator : public std::runtime_error {
        invalid_communicator(const std::string& s) :
            std::runtime_error(
                std::string("Attempting to send: \"") + s 
                    + "\" but communicator is invalid or not set"
            ) {}
    };  

    sender() : comms_() {}

    void set_communicator(std::weak_ptr<communicator> comms) {
        comms_ = comms;
    }

    /* non-const */
    void send(const std::string& s) throw (invalid_communicator)
    {
        try {
            auto sh = std::shared_ptr<communicator>(comms_);
            sh->send(s);
        } catch (const std::bad_weak_ptr& e) {
            comms_ = decltype(comms_)();
            throw invalid_communicator(s);
        }
    }

private:
    std::weak_ptr<communicator> comms_;
};

int main() {
    int rv = -1;
    sender mysender;

    for (auto com : {1, 2, 3}) {
        try {
            { 
                // create comms object
                auto comms = std::make_shared<communicator>(
                    "myname", com, 9600
                );
                mysender.set_communicator(comms);
                mysender.send("Hi guys!");
            }// comms object gets deleted here

            mysender.send("Hi guys after ptr delete!"); 

            // never reached in this example; just to illustrate
            // how the story could continue  
            rv = EXIT_SUCCESS;            
            break; // it'd be not nice to "break", but I did not want to
                   // introduce another state variable
        } catch (const sender::invalid_communicator& e) {
            std::cerr << e.what() << std::endl;
        }
    }

    return rv;
}

一個Coliru's

這是使用std智能指針確保ptr安全的正確方法

除了decltype_auto所提到的內容之外,我可以補充一點,使用weak_ptr的原因是防止循環依賴。 如果不存在這種可能性,你也可以讓它共享,這會使發送的實現更不容易出錯,除非通信通道的生命周期真的是暫時的。

您可以隱藏它們在實現中存在各種連接或會話的事實。

此外,在使用標准智能指針設計接口/ API時,請考慮使用更為受限制的指針,如unique_pointer。

這樣的指針非常清楚地傳達意圖 - 例如,通過將唯一指針作為函數的參數,您可以清楚地告訴用戶他正在將指向資源的所有權交給被調用函數。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM