Caveat: This question arose because I have to work with a huge pile of awful code, without proper documentation, written by someone else as a research project 6 years ago. Obviously a better solution is not to cause these issues in the first place, with proper design…
That said the question is: What is the best way to get out of a situation like this:
- A class allocates memory on the heap, and frees it in the destructor.
- Somewhere, an instance of the class is declared at global scope.
- A function exists that initializes this instance.
- That function’s return value is used to initialize a static variable.
- The globally scoped variable is used outside of the static scoping.
Minimal working example:
File “myclass.h”:
#ifndef MYCLASS_H
#define MYCLASS_H
#include<vector>
using namespace std;
class myclass{
vector<int> *onTheHeap;
public:
myclass(int len=0){
onTheHeap = new vector<int>(len);
}
~myclass(){
delete onTheHeap;
}
};
#endif
file “static_loader.cpp”
#include "class.h"
myclass existsForever;
int cause_static_global_creation(){
existsForever = myclass(5);
}
static int bootstrap = cause_static_global_creation();
and file “main.cpp”:
#include "class.h"
extern myclass existsForever;
int main(){
return 0;
}
Build with:
g++ -g -c static_loader.cpp
g++ -g main.cpp static_loader.o
and run as:
valgrind --leak-check=full ./a.out
Result: The variable is freed when its destructor is called in the exit handler’s below main, but also in the static_initialization_and_destruction_0 function below main from static_loader!
Is there a way to ensure that these variables are freed exactly once that doesn’t involve re factoring the code extensively? In the library I have to work with, there are several dozen instances of this pattern…
EDIT:
Adding functions:
void operator=(myclass other){
delete this->onTheHeap;
this->onTheHeap = other.onTheHeap;
}
and
myclass(const myclass& other){
this->onTheHeap = new vector<int>(*(other.onTheHeap));
}
Does not change the behavior.
Second EDIT:
myclass& operator=(const myclass& other){
delete this->onTheHeap;
this->onTheHeap = new vector<int>(*(other.onTheHeap));
return *this;
}
Resolves all the issues. My library has a memory leak with sources like this anyway, but I’m no longer sure how to reproduce it. At least it’s not this though, and thanks for the suggestions on refactoring etc. as well!
Your assumption is broken.
myclass existsForever;is initialized not bycause_static_global_creation, but bymyclass::myclass. Instead,cause_static_global_creationassigns a value to the already-initialized object.And as the class violates the Rule Of Three, it’s no surprise that the assignment causes issues.