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

R. *_*. N 0 c++ shared-ptr

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

我使用 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;
}
Run Code Online (Sandbox Code Playgroud)

然后使用编译行:

#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;
}
Run Code Online (Sandbox Code Playgroud)

我得到以下输出:

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
Run Code Online (Sandbox Code Playgroud)

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

Rem*_*eau 6

在这一行:

mapTest.set("length", &length);
Run Code Online (Sandbox Code Playgroud)

您正在使用指向未分配的局部变量的set()原始指针进行调用。在内部,将原始指针按原样分配给. 当它稍后被销毁时,它将尝试调用该指针,但由于它指向的指针最初并未分配,因此您会收到运行时错误。double*lengthnewset()shared_ptrmapshared_ptrdeletedouble*doublenew

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

#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;
}
Run Code Online (Sandbox Code Playgroud)

在线演示