Por que não posso chamar o construtor da minha classe a partir de uma instância dessa classe em C++?

StackOverflow https://stackoverflow.com/questions/2146901

  •  23-09-2019
  •  | 
  •  

Pergunta

Quando um objeto de uma classe pode chamar o destruidor dessa classe, como se fosse uma função regular?Por que não pode chamar o construtor da mesma classe, como uma de suas funções regulares?Por que o compilador nos impede de fazer isso?

Por exemplo:

class c
{
public:
   void add() ;
   c();
   ~c() ;
};

void main()
{
 c objC  ;
 objC.add() ;
 objC.~c() ; // this line compiles
 objC.c() ;  // compilation error
}
Foi útil?

Solução

Por definição, um construtor é chamado apenas uma vez, quando o objeto é criado. Se você tem acesso a um objeto, ele deve ter sido criado, para que você não tenha permissão para ligar para o construtor novamente - é por isso que as chamadas explícitas do construtor não são permitidas. Da mesma forma, os destruidores devem ser chamados apenas uma vez, quando o objeto é destruído. Se isso sempre pudesse ser feito automaticamente, o idioma também proibiria chamadas explícitas de destruidores.

No entanto, em algumas circunstâncias, você pode querer controle preciso sobre o gerenciamento de memória e a capacidade de criar e destruir explicitamente objetos na memória que está gerenciando. Para esse fim, o idioma fornece "posicionamento novo" para criar um objeto em um local arbitrário e chamadas explícitas de destruidores para destruir objetos criados dessa maneira. Uma chamada explícita do construtor não seria útil, pois você precisa especificar o local do novo objeto - para obter "posicionamento novo". Uma chamada explícita de destruidor é suficiente, portanto, não há necessidade de inventar algum tipo de "exclusão de posicionamento" correspondente.

Portanto: não há uso válido para chamadas explícitas de construtor, para que elas não sejam permitidas. Existe um uso válido para chamadas explícitas de destruidores, então elas são (sintaticamente) permitidas, com a regra de que você deve usá -las apenas em objetos que de outra forma não serão destruídos, ou seja, objetos criados usando "posicionamento novo" e nessa que Caso ligue para eles exatamente uma vez. Usá -los de qualquer outra maneira, como tantos erros de C ++, compilará, mas dará comportamento indefinido.

Outras dicas

Acho que você pode chamar explicitamente o destruidor se tiver certeza de que a instância foi substituída/recriada com uma chamada para o posicionamento novo:

class c
{
public:
   void add() ;
   c();
   ~c() ;
};

int main()
{
 c objC  ;
 objC.add() ;
 objC.~c() ; // this line compiles
 new (&objC) c;  // placement new invokes constructor for the given memory region
}

Nunca vi isso na prática, mas logicamente deveria funcionar (a menos que o construtor de c possa lançar, caso em que, imagino, o inferno pode acontecer durante o desenrolamento da pilha).

No entanto, o que você provavelmente deseja é apenas uma atribuição:

objC = c();

Se o destruidor tiver efeitos colaterais de seu interesse, implemente a atribuição usando o idioma copiar e trocar, que faz com que o destruidor seja invocado para o valor "esquerdo".

Um destruidor deve ser chamado em uma instância existente de uma classe - destruir a instância é o que faz. Um construtor cria uma nova instância de uma classe, portanto, pedir uma instância existente não faz sentido.

Isso é semelhante à maneira como o trabalho novo e excluído:

int * p = new int;    // call to new needs no existing instance
delete p;             // call to delete requires existing instance

E observe em seu código, o objeto seria destruído duas vezes, uma vez explicitamente, e uma vez implicitamente no final de seu escopo anexante. Você normalmente chama apenas explicitamente um destruidor se estiver fazendo algo incomum, provavelmente envolvendo o uso do posicionamento novo.

Se você realmente precisa fazer algo assim, basta criar uma função adicional e chamá -la de fora e do próprio construtor, mas vamos ver o que acontece quando você precisa de tal chamada:

#include<new>

class A
{
//members
};

int main()
{
//allocate buffer
char* buffer = new char[sizeof(A)];
//construct A on that memory space
A * ptrToA = ::new (buffer) A();
//destroy the object
ptrToA->~A();
//deallocate the buffer
delete[] buffer;
}

Uma instância em que você pode encontrar o posicionamento novo uso são os contêineres padrão. O alocador assume a responsabilidade de alocar o buffer (alocar membro) e os objetos são construídos sobre esse buffer à medida que são adicionados ao contêiner. Por exemplo, quando você se reserva em objeto vetorial, ele se reserva o espaço para N objetos que significam aloca espaço para N objetos, mas não os constrói. Então, quando você push_back etc, para adicionar elementos, eles são criados sobre esse buffer. Basicamente, é uma técnica reduzir a sobrecarga de chamadas repetidas para a função de alocação de memória. E então, quando feito, o destruidor do vetor destruiria os objetos que chamavam o destruidor explicitamente para todos os objetos nele e depois chamam a função Deallocate () do alocador para liberar a memória. Espero que isto ajude.

Experimente isso:

obj.className :: ClassName (); // funciona no compilador VC 6.0

Outra maneira de pensar sobre a restrição é que um construtor é não apenas mais uma função. Considere sua definição: diferentemente de outras funções, ela não possui valor de retorno e pode ter uma lista de inicializador. Que acontece ter a maior parte da sintaxe de uma função é uma espécie de coincidência; Realmente existe apenas com o objetivo de inicializar uma nova instância de um objeto.

O construtor está lá "c ()" usado com novo, ou seja,

c objC = new c();

Se você deseja ligar para o seu construtor fora da construção real da instância da classe, você não entendeu o objetivo do construtor ou está tentando colocar a funcionalidade lá que não deveria estar lá.

Você está perguntando sobre um estilo específico de sintaxe mais do que uma limitação de idioma. O C ++ permite chamar o construtor ou destruidor de um objeto.

c objC;
objC.~c();  // force objC's destructor to run
new(&objC); // force objC's constructor to run

Por que os designers de idiomas não usaram essa sintaxe?

c objC;
delete(&objC) c; // force objC's destructor to run
new(&objC) c;    // force objC's constructor to run

Ou por que não:

c objC
objC.~c(); // force objC's destructor to run
objC.c();  // force objC's constructor to run

Minha opinião sobre por que eles escolheram a sintaxe que fizeram: a primeira alternativa é mais flexível que a segunda. Posso chamar o construtor / destruidor em qualquer endereço, não apenas uma instância. Essa flexibilidade é necessária para a alocação, mas não para destruição. Além disso, a exclusão da primeira opção parece ser muito perigosa quando os destruidores virtuais entram na bagunça.

Você pode invocar o construtor da classe de uma instância usando typeof:

class c
{
public:
   void add() ;
   c();
   ~c() ;
};

void main()
{
 c objC  ;
 objC.add() ;
 objC.~c() ; // this line compiles (but is a bad idea)
 typeof objC otherObjC;  // so does this.
}

Isso não afeta o valor da instância objC, mas cria uma nova instância otherObjC usando objCConstrutor de classe.

NOTA: Isso pode fazer algo que você não espera se o tipo estático for um grupo de base do tipo dinâmico da instância que você possui.

Quem disse que você não pode? Você só precisa saber como.

void Foo::Bar() {
  *this = Foo(); // Reset *this
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top