paulo1205
(usa Ubuntu)
Enviado em 09/02/2022 - 19:11h
SamL escreveu:
Na verdade não era bem a linguagem, mas sim referente a STL mesmo. No caso, estou cansado de sofrer pra trabalhar com ela.
O que eu tentei fazer foi o seguinte:
Vou dar um exemplo simplorio mas não tem muito a ver com meu código (que é gigante):
class A {
public:
int * ptr;
std::map<std::string,int> mapa;
A()const std::map<std::string, int> & m): mapa(m) {
ptr = nullptr;
}
void changePtr(const std::string &nome) {
if (mapa.find(nome) != mapa.end())
ptr = &mapa[nome];
else
ptr = nullptr;
}
};
Esse foi o código que usei por anos achando que tava ok, mas descobri ontem que não estava.
Sim, você tem um ponteiro fora do mapa para uma coisa dentro do mapa. É justamente uma situação que eu falei que tem grande chance de dar problema.
Descobri que, dentro do escopo de changePtr, o ptr aponta pro mesmo endereço do mapa[nome].
Acontece que, por algum motivo macabro em outra função da mesma classe A, os endereços do conteúdo do mapa, foram trocados, mas o ptr continuou apotando pra o endereço original.
Isso aconteceria em caso de eu ter trocado o conteúdo do mapa, mas em nenhum, repito nenhum momento, eu mudei o mapa. Nem uma função pra trocar o mapa eu adicionei e ele é privado na classe.
Simplesmente mudou sozinho e isso me ferrou por várias horas.
Uma possibilidade (que não aparece no código reduzido que você colocou aqui, mas que é um uso — e um erro — muito comum é você usar o operador
[] (para acesso a elemento baseado na chave de consulta). Esse operador não é constante se o objeto ao qual você a aplicar não for uma referência constante, de modo que seu uso pode causar alteração no mapa, inclusive com realocação e movimentação de objetos.
Se o problema for esse (ou mesmo algo parecido, cujo efeito pode ser o de provocar uma inserção ou uma exclusão no mapa), então, dado que tal comportamento é documentado, o erro pode ter sido seu.
O único jeito de ter mudado o mapa, seria via construtor padrão. Só que o seguinte, o ptr terificado como nullptr, mas isso em nenhum momento aconteceu, já que o ptr continuava com o endereço antigo.
Não faço ideia do que houve, investiguei a fundo pra ver onde mudou o endereço do mapa mas não achei nada.
O mesmo comportamento aconteceu com o std::vector, conteúdo igual mas com endereço diferente e trocado depois da primeira iteração.
Pra ter certeza que tinha sido trocado, eu imprimi cada endereço e por fim, mostrou que eles estava diferentes dos endereços quando alocados pelo construtor padrão. É como se o mapa ou vector tivesse sido destruido e reconstruido no processo.
Pode ser o construtor
default de cópia. Quando você tem um membro de dados que é um ponteiro ou referência, geralmente é um erro usar um construtor de cópia padrão, pois ele copia
ipsis litteris dados de tipos nativos (incluindo ponteiros) e de tipos que não tenham construtores de cópia especializados. Raramente você quer que valores de ponteiros sejam copiados literalmente, mas sim que eles apontem para algum outro novo objeto que também pode ter sido copiado a partir de outro, não para esse objeto original.
Só vendo o programa todo para saber com certeza. Acima eu teorizei uma possibilidade. Você falou de outra, até mais provável, mas teria de ver onde essa cópia estaria ocorrendo.
Você mencionou que o programa funcionou por um longo tempo. É um típico caso do que costumo classificar como “programa que funciona por
azar ” (não por sorte), já que deixa uma clara violação de contrato de uso sem detecção e impune, dando uma ilusão de conforto ao programador, apenas para assombrá-lo no futuro de uma maneira que lhe parece quase que totalmente aleatória, embora não o seja.
Gosto de C++, mas vi que sou muito masoquista por usar tal linguagem rsrsrs Não é a primeira vez que sou tão improdutivo com ela.
Você tem o hábito de usar
smart pointers ? Veja se algo como o seguinte lhe atenderia.
class A {
private:
shared_ptr<int> ptr;
std::map<std::string, std::shared_ptr<int>> mapa; // Em ver de apontar para um inteiro, aponta para um smart pointer compartilhado que aponta para um inteiro.
public:
A(const std::map<std::string, int> &m): mapa(m), ptr(nullptr) { } // Copia o mapa m para mapa.
A(const A &other): mapa(other.mapa), ptr(nullptr) { }
A(): mapa(), ptr(nullptr) { }
// Quando se usa um construtor de cópia não-default, geralmente o operador de atribuição também tem de ser não-default.
A &operator=(A other){
swap(this->mapa, other.mapa);
ptr=nullptr;
return *this;
}
void changePtr(const std::string &nome) {
auto i_nome=mapa.find(nome);
ptr.reset(i_nome!=mapa.end()? i_nome->second: nullptr);
}
};
Mas talvez melhor do que ter um ponteiro para o dado fosse guardar o iterador para o dado localizado.
class A {
private:
map<string, int> mapa;
decltype(mapa)::iterator i_nome;
public:
A(): mapa(), i_nome(mapa.end()) { }
bool changeIter(const string &nome){ return (i_nome=mapa.find(nome))!=mapa.end(); }
};
... Então Jesus afirmou de novo: “(...) eu vim para que tenham vida, e a tenham plenamente.” (João 10:7-10)