Comme vous l'avez apparemment déjà supposé, oui, C ++ fournit les mêmes capacités sans ce mécanisme. En tant que tel, à proprement parler, le mécanisme try
/ finally
n'est pas vraiment nécessaire.
Cela dit, s'en passer impose des exigences sur la façon dont le reste du langage est conçu. En C ++, le même ensemble d'actions est incarné dans un destructeur de classe. Cela fonctionne principalement (exclusivement?) Car l'invocation de destructeurs en C ++ est déterministe. Ceci, à son tour, conduit à des règles assez complexes sur la durée de vie des objets, dont certaines sont décidément non intuitives.
La plupart des autres langues proposent à la place une forme de collecte des ordures. Bien qu'il y ait des choses sur la collecte des ordures qui sont controversées (par exemple, son efficacité par rapport à d'autres méthodes de gestion de la mémoire), une chose n'est généralement pas: l'heure exacte à laquelle un objet sera "nettoyé" par le garbage collector n'est pas directement liée à la portée de l'objet. Cela empêche son utilisation lorsque le nettoyage doit être déterministe, soit lorsqu'il est simplement requis pour un fonctionnement correct, soit lorsqu'il s'agit de ressources si précieuses que leur nettoyage ne doit pas être retardé arbitrairement. try
/ finally
fournit un moyen pour ces langages de gérer les situations qui nécessitent ce nettoyage déterministe.
Je pense que ceux qui prétendent que la syntaxe C ++ pour cette capacité est "moins conviviale" que celle de Java manquent plutôt le point. Pire encore, il leur manque un point beaucoup plus crucial sur la division des responsabilités qui va bien au-delà de la syntaxe et qui a beaucoup plus à voir avec la façon dont le code est conçu.
En C ++, ce nettoyage déterministe se produit dans le destructeur de l'objet. Cela signifie que l'objet peut être (et devrait normalement être) conçu pour se nettoyer après lui-même. Cela rejoint l'essence de la conception orientée objet - une classe doit être conçue pour fournir une abstraction et appliquer ses propres invariants. En C ++, c'est exactement ce que l'on fait - et l'un des invariants qu'il prévoit est que lorsque l'objet est détruit, les ressources contrôlées par cet objet (toutes, pas seulement la mémoire) seront détruites correctement.
Java (et similaire) est quelque peu différent. Bien qu'ils prennent (en quelque sorte) en charge un finalize
qui pourrait théoriquement fournir des capacités similaires, le support est si faible qu'il est fondamentalement inutilisable (et en fait, pratiquement jamais utilisé).
Par conséquent, plutôt que la classe elle - même puisse effectuer le nettoyage requis, le client de la classe doit prendre des mesures pour ce faire. Si nous faisons une comparaison suffisamment courte, il peut sembler à première vue que cette différence est assez mineure et Java est assez compétitif avec C ++ à cet égard. Nous nous retrouvons avec quelque chose comme ça. En C ++, la classe ressemble à ceci:
class Foo {
// ...
public:
void do_whatever() { if (xyz) throw something; }
~Foo() { /* handle cleanup */ }
};
... et le code client ressemble à ceci:
void f() {
Foo f;
f.do_whatever();
// possibly more code that might throw here
}
En Java, nous échangeons un peu plus de code où l'objet est utilisé pour un peu moins dans la classe. Cela ressemble initialement à un compromis assez uniforme. En réalité, c'est loin d'être le cas, car dans la plupart des codes, nous ne définissons la classe qu'à un seul endroit, mais nous l' utilisons à plusieurs endroits. L'approche C ++ signifie que nous écrivons uniquement ce code pour gérer le nettoyage en un seul endroit. L'approche Java signifie que nous devons écrire ce code pour gérer le nettoyage plusieurs fois, à de nombreux endroits - chaque endroit où nous utilisons un objet de cette classe.
En bref, l'approche Java garantit fondamentalement que de nombreuses abstractions que nous essayons de fournir sont "fuyantes" - toutes les classes qui nécessitent un nettoyage déterministe obligent le client de la classe à connaître les détails de ce qu'il faut nettoyer et comment faire le nettoyage , plutôt que ces détails étant cachés dans la classe elle-même.
Bien que je l'ai appelé "l'approche Java" ci-dessus, try
/ finally
et des mécanismes similaires sous d'autres noms ne sont pas entièrement limités à Java. Pour un exemple frappant, la plupart (tous?) Des langages .NET (par exemple, C #) fournissent le même.
Les itérations récentes de Java et de C # fournissent également quelque chose à mi-chemin entre Java «classique» et C ++ à cet égard. En C #, un objet qui souhaite automatiser son nettoyage peut implémenter l' IDisposable
interface, qui fournit une Dispose
méthode qui est (au moins vaguement) similaire à un destructeur C ++. Bien que cela puisse être utilisé via un try
/ finally
like en Java, C # automatise un peu plus la tâche avec une using
instruction qui vous permet de définir les ressources qui seront créées lors de la saisie d'une étendue, et détruites lorsque la portée sera fermée. Bien que toujours bien en deçà du niveau d'automatisation et de certitude fourni par C ++, il s'agit toujours d'une amélioration substantielle par rapport à Java. En particulier, le concepteur de classe peut centraliser les détails de la façon dontde disposer de la classe dans son implémentation IDisposable
. Tout ce qui reste pour le programmeur client est le moindre fardeau d'écrire une using
déclaration pour s'assurer que l' IDisposable
interface sera utilisée quand elle devrait l'être. Dans Java 7 et plus récent, les noms ont été modifiés pour protéger les coupables, mais l'idée de base est fondamentalement identique.