为什么我的 shared_ptr 变成了无效指针?

Why did my shared_ptr turn into an invalid pointer?

我正在尝试在地图中存储一些派生的 类。

我使用 share_ptr 存储它们以避免意外的重新分配。

不幸的是,在我的尝试中它运行正常:程序编译并执行但我收到一条错误消息。

我获得了以下MWE:

#include <iostream>
#include <map>
#include <memory>
#include <string>
#include <typeindex>
#include <typeinfo>

using namespace std;

class DataInOut {
public:
    std::shared_ptr<void> data = nullptr;
    std::type_index type = typeid(nullptr);
    bool initialized = false;
    bool optional = false;

    // template <class Archive>
    virtual bool dummy_funct(int &ar, const char* charName){
        cout<< "serialize_or_throw from DataInOut address is an empty function." << endl; 
        return true;
    }

    DataInOut *clone() const { return new DataInOut(*this); }

    ~DataInOut(){}; // Destructor
};

template <typename T>
class DataInOutType : public DataInOut {
public: 
    // template <class Archive>
    bool dummy_funct(int &ar, const char* charName){
        cout<< "serialize_or_throw from DataInOutTYPE is an FULL function." << endl;
        return true;
    }
}; 

class mapClass {
private:
    std::map<std::string, std::shared_ptr<DataInOut> > _m;
    
public:

    template<typename T>
    void set(string key, T* var, bool optional = false) {
        cout << "set entry with " << var << endl;
        std::shared_ptr<DataInOutType<T>> dataIO_ptr (new DataInOutType<T>);
        dataIO_ptr->type = typeid(*var) ;
        dataIO_ptr->data.reset( var ) ;
        dataIO_ptr->optional = optional ;
        dataIO_ptr->initialized = true ;
        _m.insert( std::pair<std::string, std::shared_ptr<DataInOutType<T>>>(key, dataIO_ptr) );
        int toto= 1;
        // dataIO_ptr.get()->dummy_funct(toto, key.c_str());
        // cout << "set EXIT" << endl;
    }

    void call_dummy(string key){
        int dummyArchive= 1;
        _m.at(key).get()->dummy_funct(dummyArchive, key.c_str());
    }
};

int main(int argc, const char *argv[]) {
    cout << "Hello World!" << endl;

    mapClass mapTest;
    double length = 1.0;

    mapTest.set("length", &length);
    cout << "mapTest is out of set" << endl;
    mapTest.call_dummy("length");

    cout << "ByeBye!" << endl;
    return 0;
}

然后使用编译行:

g++ -std=c++17 ./test.cpp -o prog && ./prog

我得到以下输出:

Hello World!
set entry with 0x7ffcda122318
mapTest is out of set
serialize_or_throw from DataInOutTYPE is an FULL function.
ByeBye!
free(): invalid pointer
Abandon

所以我的问题是如何防止无效指针?

这一行:

mapTest.set("length", &length);

您正在使用原始 double* 指针调用 set(),该指针指向未使用 new 分配的局部变量 length。在内部,set() 将原始指针 as-is 分配给 map 中的 shared_ptr。当 shared_ptr 稍后被销毁时,它将尝试在 double* 指针上调用 delete,但由于它指向的 double 未分配给 new首先,您会遇到运行时错误。

这就是为什么在没有清晰的所有权语义的情况下将原始指针与智能指针混合使用是非常危险的。仅将原始指针用于现有数据的 non-owning 视图。动态分配数据时使用智能指针。例如:

#include <iostream>
#include <map>
#include <memory>
#include <string>
#include <typeindex>
#include <typeinfo>

using namespace std;

class DataInOut {
public:
    shared_ptr<void> data = nullptr;
    type_index type = typeid(nullptr);
    bool initialized = false;
    bool optional = false;

    // template <class Archive>
    virtual bool dummy_funct(int &ar, const char* charName){
        cout<< "serialize_or_throw from DataInOut address is an empty function." << endl; 
        return true;
    }

    virtual shared_ptr<DataInOut> clone() const { return make_shared<DataInOut>(*this); }

    virtual ~DataInOut() = default; // Destructor
};

template <typename T>
class DataInOutType : public DataInOut {
public: 
    // template <class Archive>
    bool dummy_funct(int &ar, const char* charName){
        cout << "serialize_or_throw from DataInOutTYPE is an FULL function." << endl;
        return true;
    }

    shared_ptr<DataInOut> clone() const override { return make_shared<DataInOutType<T>>(*this); }
}; 

class mapClass {
private:
    map<string, shared_ptr<DataInOut> > _m;
    
public:

    template<typename T>
    void set(const string &key, shared_ptr<T> var, bool optional = false) {
        cout << "set entry with " << var.get() << endl;
        auto dataIO_ptr = make_shared<DataInOutType<T>>();
        dataIO_ptr->type = typeid(T);
        dataIO_ptr->data = var;
        dataIO_ptr->optional = optional;
        dataIO_ptr->initialized = true;
        _m[key] = dataIO_ptr;
        int toto = 1;
        // dataIO_ptr->dummy_funct(toto, key.c_str());
        // cout << "set EXIT" << endl;
    }

    void call_dummy(const string &key){
        int dummyArchive = 1;
        _m.at(key)->dummy_funct(dummyArchive, key.c_str());
    }
};

int main() {
    cout << "Hello World!" << endl;

    mapClass mapTest;
    auto length = make_shared<double>(1.0);

    mapTest.set("length", length);
    cout << "mapTest is out of set" << endl;
    mapTest.call_dummy("length");

    cout << "ByeBye!" << endl;
    return 0;
}

Online Demo