Verificando se isso é nulo

Alguma vez faz sentido verificar se isso é nulo?

Digamos que eu tenha uma aula com um método; dentro desse método, eu verifico this == NULL , e se for, retorne um código de erro.

Se isso for nulo, significa que o object foi excluído. O método é capaz de retornar alguma coisa?

Atualização: Esqueci de mencionar que o método pode ser chamado de vários encadeamentos e pode fazer com que o object seja excluído enquanto outro encadeamento estiver dentro do método.

Faz algum sentido verificar isso == null? Eu encontrei isso ao fazer uma revisão de código.

No padrão C ++, isso não acontece, porque qualquer chamada em um ponteiro nulo já é um comportamento indefinido, portanto, qualquer código que dependa de tais verificações não é padrão (não há garantia de que a verificação será executada).

Observe que isso também é válido para funções não virtuais.

Algumas implementações permitem this==0 , no entanto, e consequentemente, bibliotecas escritas especificamente para essas implementações, às vezes, o usarão como um hack. Um bom exemplo de tal par é VC ++ e MFC – não lembro o código exato, mas lembro claramente de ver if (this == NULL) verifica o código-fonte do MFC em algum lugar.

Ele também pode estar lá como um auxílio de debugging, porque em algum momento no passado esse código foi atingido com this==0 por causa de um erro no chamador, então uma verificação foi inserida para capturar instâncias futuras disso. Uma declaração faria mais sentido para essas coisas, no entanto.

Se isso == nulo, significa que o object foi excluído.

Não, isso não significa isso. Isso significa que um método foi chamado em um ponteiro nulo ou em uma referência obtida de um ponteiro nulo (embora a obtenção dessa referência já seja UB). Isso não tem nada a ver com delete e não requer que nenhum object desse tipo tenha existido.

Sua nota sobre tópicos é preocupante. Tenho certeza que você tem uma condição de corrida que pode levar a uma falha. Se um encadeamento excluir um object e zeros o ponteiro, outro encadeamento poderia fazer uma chamada por meio desse ponteiro entre essas duas operações, levando a que this não seja nulo e também não seja válido, resultando em uma falha. Da mesma forma, se um thread chamar um método enquanto outro thread estiver no meio da criação do object, você também poderá obter uma falha.

Resposta curta, você realmente precisa usar um mutex ou algo para sincronizar o access a essa variável. Você precisa garantir que this nunca é nulo ou você terá problemas.

FWIW, eu usei verificações de debugging para (this != NULL) em afirmações antes que ajudaram a pegar o código defeituoso. Não que o código tenha necessariamente ficado longe demais sem uma falha, mas em pequenos sistemas embarcados que não têm proteção de memory, as afirmações realmente ajudaram.

Em sistemas com proteção de memory, o sistema operacional geralmente atingirá uma violação de access se chamado com um ponteiro NULL, portanto, há menos valor em afirmar this != NULL . No entanto, veja o comentário de Pavel sobre por que não é necessariamente inútil em sistemas protegidos.

Eu sei que isso é antigo, mas eu sinto que agora estamos lidando com C + + 11-17 alguém deveria mencionar lambdas. Se você capturar isso em um lambda que será chamado de forma assíncrona em um momento posterior , é possível que seu object “this” seja destruído antes que o lambda seja invocado.

ou seja, passá-lo como um retorno de chamada para alguma function cara-hora que é executada a partir de um thread separado ou apenas de forma assíncrona em geral

EDIT: Só para ficar claro, a pergunta foi “Será que faz algum sentido para verificar se isso é nulo” estou apenas oferecendo um cenário onde faz sentido que pode se tornar mais prevalente com o uso mais amplo do C ++ moderno.

Exemplo criado: esse código é totalmente executável. Para ver um comportamento inseguro, apenas comente a chamada para um comportamento seguro e descomente a chamada de comportamento inseguro.

 #include  #include  #include  #include  class SomeAPI { public: SomeAPI() = default; void DoWork(std::function cb) { DoAsync(cb); } private: void DoAsync(std::function cb) { std::cout << "SomeAPI about to do async work\n"; m_future = std::async(std::launch::async, [](auto cb) { std::cout << "Async thread sleeping 10 seconds (Doing work).\n"; std::this_thread::sleep_for(std::chrono::seconds{ 10 }); // Do a bunch of work and set a status indicating success or failure. // Assume 0 is success. int status = 0; std::cout << "Executing callback.\n"; cb(status); std::cout << "Callback Executed.\n"; }, cb); }; std::future m_future; }; class SomeOtherClass { public: void SetSuccess(int success) { m_success = success; } private: bool m_success = false; }; class SomeClass : public std::enable_shared_from_this { public: SomeClass(SomeAPI* api) : m_api(api) { } void DoWorkUnsafe() { std::cout << "DoWorkUnsafe about to pass callback to async executer.\n"; // Call DoWork on the API. // DoWork takes some time. // When DoWork is finished, it calls the callback that we sent in. m_api->DoWork([this](int status) { // Undefined behavior m_value = 17; // Crash m_data->SetSuccess(true); ReportSuccess(); }); } void DoWorkSafe() { // Create a weak point from a shared pointer to this. std::weak_ptr this_ = shared_from_this(); std::cout << "DoWorkSafe about to pass callback to async executer.\n"; // Capture the weak pointer. m_api->DoWork([this_](int status) { // Test the weak pointer. if (auto sp = this_.lock()) { std::cout << "Async work finished.\n"; // If its good, then we are still alive and safe to execute on this. sp->m_value = 17; sp->m_data->SetSuccess(true); sp->ReportSuccess(); } }); } private: void ReportSuccess() { // Tell everyone who cares that a thing has succeeded. }; SomeAPI* m_api; std::shared_ptr m_data = std::shared_ptr(); int m_value; }; int main() { std::shared_ptr api = std::make_shared(); std::shared_ptr someClass = std::make_shared(api.get()); someClass->DoWorkSafe(); // Comment out the above line and uncomment the below line // to see the unsafe behavior. //someClass->DoWorkUnsafe(); std::cout << "Deleting someClass\n"; someClass.reset(); std::cout << "Main thread sleeping for 20 seconds.\n"; std::this_thread::sleep_for(std::chrono::seconds{ 20 }); return 0; } 

Seu método provavelmente (pode variar entre compiladores) poderá ser executado e também poderá retornar um valor. Contanto que não acesse nenhuma variável de instância. Se tentar isso, ele irá falhar.

Como outros salientaram, você não pode usar este teste para ver se um object foi excluído. Mesmo que você pudesse, não funcionaria, porque o object pode ser excluído por outro encadeamento logo após o teste, mas antes de você executar a próxima linha após o teste. Use a synchronization de threads.

Se this for nulo, há um bug no seu programa, provavelmente no design do seu programa.

Eu sei que esta é uma pergunta antiga, no entanto eu pensei que vou compartilhar minha experiência com o uso da captura Lambda

 #include  #include  using std::unique_ptr; using std::make_unique; using std::cout; using std::endl; class foo { public: foo(int no) : no_(no) { } template  void lambda_func(Lambda&& l) { cout << "No is " << no_ << endl; l(); } private: int no_; }; int main() { auto f = std::make_unique(10); f->lambda_func([f = std::move(f)] () mutable { cout << "lambda ==> " << endl; cout << "lambda <== " << endl; }); return 0; } 

Este código falha no segmento

 $ g++ -std=c++14 uniqueptr.cpp $ ./a.out Segmentation fault (core dumped) 

Se eu remover a instrução std::cout de lambda_func O código é executado até a conclusão.

Parece que, esta declaração f->lambda_func([f = std::move(f)] () mutable { processa as capturas de lambda antes que a function de membro seja invocada.

Eu também adicionaria que normalmente é melhor evitar null ou NULL. Eu acho que o padrão está mudando mais uma vez aqui, mas por enquanto 0 é realmente o que você quer verificar para ter absoluta certeza de que você está recebendo o que deseja.

Este é apenas um ponteiro passado como o primeiro argumento para uma function (que é exatamente o que faz dele um método). Contanto que você não esteja falando sobre methods virtuais e / ou inheritance virtual, então sim, você pode se encontrar executando um método de instância, com uma instância nula. Como outros disseram, você quase certamente não chegará muito longe com essa execução antes que os problemas surjam, mas a codificação robusta provavelmente deve verificar essa situação, com uma afirmação. Pelo menos, faz sentido quando você suspeita que poderia estar ocorrendo por algum motivo, mas precisa rastrear exatamente em que pilha de class / chamada está ocorrendo.

Isso == NULL poderia ser útil para ter um comportamento de fallback (por exemplo, um mecanismo de delegação de alocador opcional que faria fallback para malloc / free). Eu não tenho certeza de seu padrão, mas se você nunca chamar qualquer function virtual nele e, claro, nenhum membro acessar dentro do método nesse ramo, não há razão real para falhar. Caso contrário, você tem um compilador muito pobre que usa o ponteiro de function virtual quando não precisa ser e, antes de seguir estritamente o padrão, é melhor alterar seu compilador.

Às vezes é útil porque a legibilidade e a refatoração podem, em muito poucos casos, vencer o padrão (quando seu comportamento obviamente não é indefinido para todos os compiladores existentes).

Sobre o artigo: “Ainda comparando” este “ponteiro para nulo” talvez a razão é que o autor do artigo tem menos compreensão do que um compilador faz do que a equipe de software da Microsoft que escreveu o MFC.