'Why did my shared_ptr turn into an invalid pointer?
I am trying to store in a map some derived classes.
I store them using share_ptr to avoid unexpected deallocation.
Unfortunately, in my attempt it is working-ish: the program compiles and execute but I get an error message.
I obtained the following 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;
}
Then using the compilation line:
g++ -std=c++17 ./test.cpp -o prog && ./prog
I obtain the following output:
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
So my question is how to prevent from the invalid pointer ?
Solution 1:[1]
On this line:
mapTest.set("length", &length);
You are calling set() with a raw double* pointer to a local variable length that was not allocated with new. Internally, set() is assigning that raw pointer as-is to a shared_ptr in the map. When that shared_ptr is destroyed later, it will try to call delete on that double* pointer, but since the double it points to was not allocated with new to begin with, you get the runtime error.
This is why mixing raw pointers with smart pointers without clear ownership semantics is very dangerous. Use raw pointers only for non-owning views into existing data. Use smart pointers when allocating data dynamically. For example:
#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;
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
