2010-01-27 4 views
0

Quand un objet d'une classe peut-il appeler le destructeur de cette classe, comme s'il s'agissait d'une fonction normale? Pourquoi ne peut-il pas appeler le constructeur de la même classe, comme l'une de ses fonctions régulières? Pourquoi le compilateur nous arrête-t-il de le faire?Pourquoi ne puis-je pas appeler le constructeur de ma classe à partir d'une instance de cette classe en C++?

Par exemple:

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

void main() 
{ 
c objC ; 
objC.add() ; 
objC.~c() ; // this line compiles 
objC.c() ; // compilation error 
} 
+1

Vous semblez très persistant à propos de l'appel du constructeur. Si vous essayez de faire cela pour un vrai code de production, ne le faites pas. C'est moche et hack, et vous feriez mieux de faire un nouvel objet (après tout, c'est ce que vous essayez logiquement de faire). – GManNickG

+0

Ce n'est pas le code de production GMan, sa curiosité sur les raisons pour lesquelles les choses ne sont pas autorisées de cette façon. La curiosité de savoir ce qui était dans l'esprit du programmeur qui a conçu et codé le compilateur. Afin que nous puissions mieux comprendre comment les choses fonctionnent en interne dans un compilateur. :) Mike a très bien répondu, je suppose. J'ai accepté cette réponse. –

Répondre

5

Par définition, un constructeur n'est appelé qu'une seule fois, lorsque l'objet est créé. Si vous avez accès à un objet, alors il doit avoir été créé, vous n'êtes donc pas autorisé à rappeler le constructeur - c'est la raison pour laquelle les appels au constructeur explicites ne sont pas autorisés. De même, les destructeurs ne doivent être appelés qu'une seule fois, lorsque l'objet est détruit. Si cela pouvait toujours être fait automatiquement, le langage interdirait également les appels destructeurs explicites. Cependant, dans certaines circonstances, vous pouvez avoir besoin d'un contrôle précis de la gestion de la mémoire et de la possibilité de créer et de détruire explicitement des objets dans la mémoire que vous gérez. Dans ce but, le langage fournit un "placement nouveau" pour créer un objet à un emplacement arbitraire, et des appels destructeurs explicites pour détruire les objets créés de cette manière. Un appel de constructeur explicite ne serait pas utile, car vous devez être en mesure de spécifier l'emplacement du nouvel objet - donc vous obtenez plutôt le "placement new". Un appel de destructeur explicite est suffisant, il n'est donc pas nécessaire d'inventer une sorte de "placement delete" correspondant. Donc: il n'y a pas d'utilisation valide pour les appels constructeurs explicites, donc ils ne sont pas autorisés. Il existe une utilisation valide pour les appels destructeurs explicites, donc ils sont autorisés (syntaxiquement), avec la règle que vous ne devez les utiliser que sur des objets qui ne seront pas détruits autrement, c'est-à-dire des objets créés avec "placement new". cas, appelez-les exactement une fois. Les utiliser de toute autre manière, comme beaucoup d'erreurs C++, compilera mais donnera un comportement indéfini.

+0

+1 pour la réponse formelle précise. Une question comme celle-ci devrait juste être répondu par "vous ne pouvez pas, parce que ..." – dgraziotin

+0

Merci pour cette réponse Mike :) Cela correspond logiquement pourquoi l'appel explicite au constructeur n'est pas autorisé et pourquoi il est autorisé pour le destructeur. Encore une fois merci d'expliquer en détail à ma question :) –

3

Un destructor doit être appelée sur une instance existante d'une classe - destructing l'instance est ce qu'il fait. Un constructeur crée une nouvelle instance d'une classe, donc appeler une instance existante n'a aucun sens.

Ceci est similaire à la façon nouvelle et supprimer le travail:

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

Et notez dans votre code, l'objet serait détruit deux fois, une fois explicitement, et une fois implicitement à la fin de sa portée englobante. En général, vous n'appelez explicitement un destructeur que si vous faites quelque chose d'inhabituel, ce qui implique probablement l'utilisation du placement new.

+0

pourquoi ne pas cant constructeur d'appel existant comme une fonction régulière, pourquoi compilateur nous arrête de le faire ??? Une fois que l'objet est créé et quand j'appelle le destructeur explicitement, complier le permet, mais pourquoi le compilateur ne me permet pas de faire la même chose pour le constructeur. Le compilateur nous empêche de faire ça pour certaines raisons, je veux connaître cette raison. –

+0

Le chaînage de constructeur a du sens et plusieurs autres langages le supportent (Java/C#) mais pas C++. –

+1

C'est ainsi que le langage a été conçu pour fonctionner. Si vous voulez construire un objet en plus d'une version antérieure, vous devez probablement utiliser le placement new. –

2

Si vous avez vraiment besoin de faire quelque chose comme cela, il suffit de créer une fonction supplémentaire et appeler de l'extérieur et du constructeur lui-même, mais nous allons voir ce qui se passe lorsque vous avez besoin d'un tel appel:

#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; 
} 

Un exemple où vous pouvez trouver une nouvelle utilisation de placement est les conteneurs standard . L'allocateur prend la responsabilité d'allouer le tampon (membre d'allocation) et les objets sont construits sur ce tampon au fur et à mesure qu'ils sont ajoutés dans le conteneur. Par exemple, lorsque vous réservez sur un objet vectoriel, il réserve l'espace pour N objets, ce qui signifie que alloue de l'espace pour N objets mais ne les construit pas. Puis quand vous faites push_back etc, pour ajouter des éléments, ils sont créés sur ce tampon . Fondamentalement, c'est une technique pour réduire la surcharge de appels répétés à la fonction d'allocation de mémoire. Une fois terminé, le destructeur de vecteurs détruit les objets appelant le destructeur explicitement pour tous les objets qu'il contient, puis appelle la fonction deallocate() de l'allocateur pour libérer la mémoire. J'espère que cela t'aides.

+0

Cela fait longtemps que je n'ai pas fait de C++ mais je ne me souviens pas d'avoir besoin d'allouer de l'espace tampon pour instancier un objet de classe. Est-ce l'objectif C ou similaire? – Lazarus

+0

L'espace tampon n'a pas besoin d'être alloué dynamiquement, mais il doit être valide, par exemple 'char buffer [sizeof (A)]; A * p = new (buffer) A; 'Il ne s'agit pas d'instancier un objet, il s'agit d'instancier un objet à un endroit particulier en mémoire, de sorte qu'un appel explicite à un destructeur soit valide. – visitor

+0

@visitor: Il doit être alloué dynamiquement pour résoudre les problèmes d'alignement. – GManNickG

0

Le constructeur est là « c() » utilisé avec le nouveau, à savoir

c objC = new c(); 

Si vous voulez appeler votre constructeur en dehors de la construction proprement dite de l'instance de la classe alors soit vous ne l'avez pas compris le but du constructeur ou essayez d'y mettre des fonctionnalités qui ne devraient pas être là.

+2

objC vaut mieux être un pointeur –

+0

pourquoi ne pas cant existant constructeur d'appel d'objet comme une fonction régulière, pourquoi le compilateur nous arrête de le faire ??? Une fois que l'objet est créé et quand j'appelle le destructeur explicitement, complier le permet, mais pourquoi le compilateur ne me permet pas de faire la même chose pour le constructeur. Le compilateur nous empêche de faire ça pour quelques raisons je suppose, je veux savoir cette raison. –

+0

Vous pouvez utiliser le placement nouveau pour l'avoir construit en place .. nouveau (& objC) c (objC), je pense. – ScaryAardvark

0

vous pouvez appeler le constructeur d'utiliser typeof la classe d'une instance:

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

Cela n'affecte pas la valeur de l'instance objC, mais crée une nouvelle instance otherObjC utilisant constructeur de la classe de objC.

Remarque: cela peut faire quelque chose que vous ne vous attendez pas si le type statique est une classe de base du type dynamique de l'instance que vous avez.

+1

... entraînant l'invocation du destructeur deux fois pour objC. De plus, typeof est l'extension de langage de GCC, et avec la parenthèse otherObjC est une fonction qui retourne un 'c'. – UncleBens

+0

Je me demandais si c'était une extension gcc, merci. Pourquoi dites-vous que le destructeur est appelé deux fois d'objC? Je suis sûr que le destructeur est appelé une fois pour objC et une fois pour otherObjC. Je vais aller vérifier –

+0

la double destruction vient du jeûne que objC est automatique, et ~ c est destricity explicite. C'était dans le code original - le seul changement que j'ai fait était à la ligne qu'il a dit ne pas compiler, j'ai laissé le reste tel quel (et cassé). –

0

Essayez ceci:

obj.ClassName :: ClassName(); // cela fonctionne dans VC 6.0 compilateur

+0

nouvelles flash: VC 6.0 vous permet pour faire des choses qui sont: gasp: non-standard. –

4

Je pense que vous pouvez appeler explicitement le destructeur si vous assurez-vous que l'instance est remplacé/recréée par un appel à un placement nouveau:

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 
} 

Je ne l'ai jamais vu En pratique, mais cela devrait logiquement fonctionner (à moins que le constructeur de c ne puisse lancer, dans ce cas, j'imagine, l'enfer pourrait se décoller pendant le déroulement de la pile).

Cependant, ce que vous voulez sans doute est juste affectation:

objC = c(); 

Si le destructor a des effets secondaires que vous êtes intéressé, mettre en œuvre la cession en utilisant l'idiome copier-échange qui obtient le destructor invoqué pour la valeur "left-hand".

0

Une autre façon de penser à la restriction est qu'un constructeur est et non juste une autre fonction. Considérez sa définition: contrairement à d'autres fonctions, il n'a pas de valeur de retour, et il peut avoir une liste d'initialisation. Il se trouve que le fait d'avoir la plus grande partie de la syntaxe d'une fonction est une sorte de coïncidence; il n'existe réellement que dans le but d'initialiser une nouvelle instance d'un objet.

+0

Mais le destructeur n'est pas seulement une autre fonction, mais vous pouvez l'appeler. – UncleBens

+0

Le destructeur n'est pas comme une fonction et pas comme un constructeur. Un destructeur peut ne pas être un constructeur virtuel, mais l'ajout de virtuel à un destructeur n'a rien à voir avec l'ajout virtuel à une méthode. C'est juste l'une des différences. –

0

Vous vous interrogez sur un style particulier de syntaxe plus qu'une limitation de langue. C++ vous permet d'appeler le constructeur ou le destructeur d'un objet. Pourquoi les concepteurs de langage n'ont-ils pas utilisé cette syntaxe à la place?

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

Ou pourquoi pas:

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

Mon opinion quant à la raison pour laquelle ils ont choisi la syntaxe qu'ils ont fait: La première alternative est plus flexible que la seconde. Je peux appeler le constructeur/destructeur sur n'importe quelle adresse et pas seulement sur une instance. Cette flexibilité est nécessaire pour l'allocation, mais pas pour la destruction. En outre, la suppression de la première option semble être très dangereuse une fois que les destructeurs virtuels arrivent dans le désordre.

0

Qui a dit que vous ne pouviez pas? Vous devez juste savoir comment.

void Foo::Bar() { 
    *this = Foo(); // Reset *this 
} 
+0

Mais cela n'appelle pas le constructeur pour 'this', il appelle' operator = '. –

+0

Évidemment non, non. Mais il appelle les classes ctor d'une instance des clas. Donc, il répond à la question posée. – MSalters

Questions connexes