Constructor predeterminado no inicializado en c++: munmap_chunk(): puntero inválido.
Teniendo este código:
#include <iostream>
#include <iterator>
#include <initializer_list>
#include <algorithm>
class Foo {
public:
Foo() = default;
explicit Foo(size_t size) :size(size){
ar = new double[size];
}
Foo(std::initializer_list<double> initList): Foo(initList.size()){
std::copy(initList.begin(), initList.end(), ar);
}
Foo(double values, size_t size):size(size), ar(values){}
Foo(const Foo &rhs): Foo(rhs.size){
std::copy(rhs.ar, rhs.ar+size, ar);
}
~Foo(){delete[] ar;}
Foo &operator=(Foo rhs){
swap(this, rhs);
return *this;
}
void print(){
std::copy(ar, ar+size, std::ostream_iterator<double>(std::cout, " "));
std::cout << std::endl;
}
private:
size_t size;
double *ar;
static void swap(Foo &f, Foo &s){
std::swap(f.size, s.size);
std::swap(f.ar, s.ar);
}
};
int main() {
using namespace std;
size_t size = 100;
auto *values = new double[size];
for(int i = 0; i<100; i++){
double fraction = ((10+i) % 10) / 10.0;
values[i] = i + fraction;
}
Foo f(values, size);
// Foo g; //IF THIS IS NOT BRACED-INITIALIZED, I GOT munmap_chunk(): invalid pointer
Foo g{};
g = f;
g.print();
}
La única diferencia entre el programa que se ejecuta y el que da error es si inicializo la clase Foo g
con llaves o no. ¿Por qué es importante? Sé que las llaves van a valor-inicializar la clase, lo que significa que el int *ar
sería nullptr
. Si no se inicializa con llaves, entonces el int *ar
es indeterminado. ¿Pero qué significa eso? ¿Cómo puede ser un puntero indeterminado? ¿Es lo mismo que nullptr
? ¿Y por qué falla el programa cuando el puntero es indeterminado?
davy.ai
Cuando
Foo g
no está inicializado con llaves, el punteroint *ar
no se inicializa, lo que significa que contiene valores basura de la memoria. Este estado del puntero se llama indeterminado, lo que significa que puede contener cualquier valor, no necesariamentenullptr
.En este caso, cuando
Foo g
no está inicializado, la variable miembrosize
del objetog
se inicializa a algún valor basura y se llama al constructorFoo(const Foo &rhs)
, que copia los valores basura derhs
ag
. Cuandog
se destruye al final demain()
, el destructor intenta eliminar el valor del puntero basura en su variable miembroar
, lo que causa un comportamiento indefinido y probablemente hace que el programa falle.Por otro lado, cuando se inicializa con llaves
Foo g{}
obtiene una inicialización de valor, lo que significa que su variable miembroint *ar
se inicializa anullptr
. Esto evita valores de puntero basura y asegura que el destructor no intentará eliminar un puntero inválido, evitando que el programa falle.En C++, las variables no inicializadas pueden causar comportamiento indefinido, por lo que siempre es importante inicializar las variables en un estado conocido.