operador new () se comporta de maneira diferente quando o operador delete () é excluído, dependendo da existência do construtor padrão

17

Criar um novo objeto da classe C com o operador new () gera um erro aqui:

class C
{
public:
    C() {}
    virtual ~C() {}

    void operator delete(void*) = delete;
};


int main()
{
    C* c = new C;
}

com C2280: 'void C::operator delete(void *)': function was explicitly deleted

Mas quando eu substituir C() {} com C() = default; ou remover a linha para que compilador insere um construtor padrão (que eu acredito que tem o mesmo efeito com = default), o código irá compilar e executar.

Quais são as diferenças entre o construtor padrão gerado pelo compilador e o construtor padrão definido pelo usuário que fazem isso acontecer?

Eu recebi alguma dica nesta postagem , mas a classe C aqui (sem construtor fornecido pelo usuário) não é trivial, pois o destruidor é virtual, certo?

Compilado com o mais recente Visual Studio, c ++ 17.

yeshjho
fonte
3
Não tenho certeza, mas acho que a diferença é que o construtor padrão énoexcept
Sebastian Redl
11
Não é possível reproduzir com g ++. Diagnóstico semelhante sobre operator delete()se o construtor é escrito manualmente ou gerado implicitamente. O que é consistente com minhas expectativas - como uma exceção pode ser lançada pela newexpressão, o compilador precisa acessar operator delete().
Peter
@SebastianRedl você está certo, a adição noexceptfará o código compilar, mas como ...?
yeshjho 12/02
11
@ Peter A exceção só pode ser lançada pelo construtor; portanto, se for noexceptcomo SebastianRedl mencionou, uma chamada para operator deletenão precisar ser incluída. Além disso, o g ++ apenas reclama se o destruidor é virtual. Caso contrário, ele sempre compila, mesmo se o construtor estiver jogando.
noz
@LeDYoM Seu link é sobre a análise de endereços IP, o que parece ser irrelevante para a pergunta. Você postou um link errado?
LF

Respostas:

17

Quais são as diferenças entre o construtor padrão gerado pelo compilador e o construtor padrão definido pelo usuário que fazem isso acontecer?

newA expressão chama o correspondente operator newe, em seguida, chama o construtor. Se o construtor lançar uma exceção, newdeverá desfazer o efeito de operator new(para evitar vazamento de memória) chamando o correspondente operator delete. Se o último for excluído, a newexpressão não poderá chamá-lo, o que resulta no compilador error: use of deleted function 'static void C::operator delete(void*)'.

Um noexceptconstrutor não pode lançar uma exceção; portanto, o correspondente operator deletenão é necessário, pois não será chamado por uma newexpressão. Um defaultconstrutor de uma classe trivial também é um noexceptconstrutor. A presença de um destruidor virtual precisa operator deleteser não excluída porque o destruidor de exclusão escalar especial (um detalhe de implementação para permitir a deleteexpressão através do ponteiro da classe base) invoca operator delete.

Parece não ser especificado pelo padrão C ++ se o compilador deve exigir operator deleteque não seja excluído, mesmo que não possa ser chamado por newexpressão. gcc, No entanto, não parece estar invocando o correspondente operator deletena newexpressão em todos, se é deleted (postaram um relatório de bug ).

Maxim Egorushkin
fonte