Aventuras de arrasar com o juizo [RESOLVIDO]

1. Aventuras de arrasar com o juizo [RESOLVIDO]

Samuel Leonardo
SamL

(usa XUbuntu)

Enviado em 08/02/2022 - 18:40h

Vim só desabafar aqui, não ligem.

C++ é uma das piores linguagens pra se trabalhar. Minha nossa cara, como essa linguagem é horrível!

Passei aqui a tarde toda tendo um ponteiro apontando pra áreas de memoria diferentes de um std::map, até ai descobri que NÃO era uma boa coisa apontar num std::map, tudo bem.
Mudei pra um std::vector. O resultado foi que, de novo, o ponteiro apontava mas o conteudo do vector mudava, assim como no map.
Cara, é de lascar o juizo, pelo amor de deus!

Resultado final: transformei o ponteiro interno na classe em um int e o map em um vector, sendo assim, o ponteiro virou um indice do vector e ai finalizou a bagaça.


  


2. MELHOR RESPOSTA

Paulo
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)

3. Re: Aventuras de arrasar com o juizo

Paulo
paulo1205

(usa Ubuntu)

Enviado em 09/02/2022 - 03:26h

SamL escreveu:

Vim só desabafar aqui, não ligem.

C++ é uma das piores linguagens pra se trabalhar. Minha nossa cara, como essa linguagem é horrível!

Passei aqui a tarde toda tendo um ponteiro apontando pra áreas de memoria diferentes de um std::map, até ai descobri que NÃO era uma boa coisa apontar num std::map, tudo bem.
Mudei pra um std::vector. O resultado foi que, de novo, o ponteiro apontava mas o conteudo do vector mudava, assim como no map.
Cara, é de lascar o juizo, pelo amor de deus!


Sem querer dar uma de advogado da linguagem, mas o que exatamente você tentou fazer? Ponteiro para o quê, dentro do mapa ou dentro do vetor?

Mesmo sem detalhes, vamos pensar juntos um pouco.

Tanto std::map quanto std::vector são classes que implementam estruturas de containers que trabalham com alocação, realocação e liberação automática de seus elementos. Só por isso, já não me parece uma boa coisa contar que dá para ter ponteiros fixos para dentro de objeto desse tipo.

Apontar num (ou a partir de, mas para algum objeto residente em outro local) std::map costuma ser OK. Apontar para dentro de um std::map ou std::vector sujeito a frequentes realocações, não.

Não sei se o que você quer é algo com a semântica semelhante a de objetos do Java, quem pode ter múltiplas referências compartilhadas para exatamente o mesmo objeto. Se for, você poderia ter usado std::shared_ptr.

Resultado final: transformei o ponteiro interno na classe em um int e o map em um vector, sendo assim, o ponteiro virou um indice do vector e ai finalizou a bagaça.


Ah! Não é legal quando se usam as coisas do jeito como elas foram projetadas para ser usadas? Para que ponteiro para dentro do vetor, se você tem os índices do vetor? Não vá dar uma de masoquista...


Eu gostaria realmente de ver o que você tentou fazer antes de poder concordar com você que C++ é uma linguagem ruim, porque está soando para mim que foi você que não possivelmente não leu devidamente a documentação antes de fazer o que você tentou fazer.

Em tempo: a STL, que é sim, alvo de muitas críticas, acompanha o padrão do C mas, tecnicamente falando, não é parte da linguagem propriamente dita. Vários frameworks possuem implementações alternativas daquilo que a STL oferece. Se você usa Qt, por exemplo, pode ver se a QMap lhe parece melhor que std::map.


... Então Jesus afirmou de novo: “(...) eu vim para que tenham vida, e a tenham plenamente.” (João 10:7-10)


4. Re: Aventuras de arrasar com o juizo [RESOLVIDO]

Samuel Leonardo
SamL

(usa XUbuntu)

Enviado em 09/02/2022 - 11:10h

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.
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.
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.

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.


5. Re: Aventuras de arrasar com o juizo [RESOLVIDO]

Samuel Leonardo
SamL

(usa XUbuntu)

Enviado em 09/02/2022 - 22:21h

Acho que descobri onde tava o efeito "macabro". Descobri que, internamente, uma das libs que estou usando deslocava memória com std::move, o que fazia o conteúdo mudar de endereço e os ponteiros apontarem pra área antiga, dai como falei que acontecia com o std::vector interno das classes.

No mais, achei interessante esse uso do shared_ptr, vou ver se uso em outra parte. Por enquanto, vou deixar com o índice do vetor já que não é afetado diretamente pelo deslocamento de memória.

Obrigado Paulo.






Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts