Extensions de mots clés C++

De RAD Studio
Aller à : navigation, rechercher

Remonter à Gestion des types de données et des concepts du langage Delphi


Cette section décrit les extensions de mots clés implémentées dans C++Builder pour supporter les bibliothèques RAD Studio.

__classid

L'opérateur __classid est utilisé par le compilateur pour générer un pointeur sur la vtable pour le nom de classe spécifié. Cet opérateur est utilisé pour obtenir la métaclasse d'une classe.

Syntaxe

__classid (classname)

Par exemple, __classid est utilisé lors de l'enregistrement des éditeurs de propriétés, des composants et des classes, et avec la méthode InheritsFrom de TObject. Le code suivant illustre l'utilisation de __classid pour créer un nouveau composant dérivé de TWinControl :

namespace Ywndctrl
{
  void __fastcall PACKAGE Register() {
    TComponentClass classes[1] = {__classid(MyWndCtrl)};
    RegisterComponents("Additional", classes, 0);
  }
}

__delphirtti

__delphirtti peut être utilisé pour obtenir à l'exécution des informations pour un type compatible Delphi spécifique. Fonctionnellement, la fonction __delphirtti est équivalente à la fonction TypeInfo disponible dans Delphi.

Exemple :

TTypeInfo *ti = __delphirtti(TMyType);

Voir aussi

__closure

Le mot clé __closure est utilisé pour déclarer un type spécial de pointeur sur une fonction membre. En C++ standard, le seul moyen d'obtenir un pointeur sur une fonction membre est d'utiliser le nom entièrement qualifié du membre, comme indiqué dans l'exemple suivant :

class base {
public:
    void func(int x) {
    };
};

typedef void(base::*pBaseMember)(int);

int main(int argc, char * argv[])
{
    base baseObject;
    pBaseMember m = &base::func; // Get pointer to member ‘func’.

    // Call ‘func’ through the pointer to member.
    (baseObject.*m)(17);
    return 0;
}

Cependant, vous ne pouvez pas assigner un pointeur sur un membre d'une classe dérivée à un pointeur sur un membre d'une classe de base. Cette règle (appelée contravariance) est illustrée dans l'exemple suivant :

class base {
public:
	void func(int x) {
	};
};

typedef void(base::*pBaseMember)(int);

class derived : public base {
public:
	void new_func(int i) {
	};
};

int main(int argc, char * argv[]) {
	derived derivedObject;
	pBaseMember m = &derived::new_func; // ILLEGAL
	return 0;
}

L'extension de mot clé __closure permet d'esquiver cette limitation, et davantage. En utilisant un closure, vous pouvez obtenir un pointeur sur une fonction membre pour un objet (par exemple, une instance particulière d'une classe). L'objet peut être quelconque, sans tenir compte de sa hiérarchie d'héritage. Le pointeur de l'objet est automatiquement utilisé lors de l'appel de la fonction membre via le closure. L'exemple suivant montre comment déclarer et utiliser un closure. Les classes de base et dérivée, fournies précédemment, sont supposées être définies.

class base {
public:
	void func(int x) {
	};
};

typedef void(base::*pBaseMember)(int);

class derived : public base {
public:
	void new_func(int i) {
	};
};

int main(int argc, char * argv[]) {
	derived derivedObject;
	void(__closure * derivedClosure)(int);

	// Get a pointer to the ‘new_func’ member.
	// Note the closure is associated with the
	// particular object, ‘derivedObject’.
	derivedClosure = derivedObject.new_func;

	derivedClosure(3); // Call ‘new_func’ through the closure.
	return 0;
}

Pour un exemple de code, voir TRegExReplace (C++).

Les closures fonctionnent aussi avec les pointeurs sur des objets, comme illustré dans cet exemple :

class base {
public:
	void func(int x) {
	};
};

typedef void(base::*pBaseMember)(int);

class derived : public base {
public:
	void new_func(int i) {
	};
};

// Closure with pointer to object
void func1(base * pObj) {
	// A closure taking an int argument and returning void.
	void(__closure * myClosure)(int);

	// Initialize the closure .
	myClosure = pObj->func;

	// Use the closure to call the member function.
	myClosure(1);
	return;
}

int main(int argc, char * argv[]) {
	derived derivedObject;
	void(__closure * derivedClosure)(int);

	derivedClosure = derivedObject.new_func; // Same as before...
	derivedClosure(3);

	// We can use pointers to initialize a closure, too.
	// We can also get a pointer to the ‘func’ member function
	// in the base class.
	func1(&derivedObject);
	return 0;
}

Attention, on transmet ici un pointeur à une instance de la classe dérivée, puis nous l'utilisons pour obtenir un pointeur sur une fonction membre de la classe de base - ce qui est interdit en C++ standard.

Les closures sont une partie clé de l'environnement C++Builder RAD Studio. Ils offrent la capacité d'assigner un gestionnaire d'événements dans l'inspecteur d'objets. Par exemple, un objet TButton a un événement appelé OnClick. Dans la classe TButton, l'événement OnClick est une propriété qui utilise l'extension de mot clé __closure dans sa déclaration. Le mot clé __closure permet d'assigner une fonction membre d'une autre classe (typiquement une fonction membre d'un objet TForm) à la propriété. Quand vous placez un objet TButton sur une fiche, puis créez un gestionnaire pour l'événement OnClick du bouton, C++Builder crée une fonction membre dans le parent TForm du bouton, et assigne cette fonction membre à l'événement OnClick de TButton. De cette façon, le gestionnaire d'événements est associé à cette instance particulière de TButton, et pas à une autre.

__property

Le mot clé __property déclare un attribut d'une classe. Les propriétés apparaissent au programmeur juste comme un autre attribut (champ) d'une classe. Néanmoins, comme sa contrepartie en Pascal Objet, le mot clé __property de C++Builder ajoute beaucoup plus de fonctionnalités que la simple consultation et modification de la valeur de l'attribut. Puisque les attributs de propriété contrôlent complètement l'accès à la propriété, il n'y a pas de restriction sur la façon dont vous implémentez la propriété dans la classe elle-même.

Syntaxe

__property type propertyName [index1Type index1 ][indexNType indexN ] = { attributes  }; 

où :

  • type est un type de donnée intrinsèque ou préalablement déclaré.
  • propertyName est un identificateur valide.
  • indexNType est un type de donnée intrinsèque ou préalablement déclaré.
  • indexN est le nom d'un paramètre index qui est transmis aux fonctions read et write de la propriété.
  • attributes est une séquence de read, write, stored ou index, séparés par des virgules.

Les paramètres indexN entre crochets sont facultatifs. S'ils sont présents, ils déclarent une propriété tableau. Les paramètres index sont transmis aux méthodes read et write de la propriété tableau.

L'exemple suivant illustre des déclarations de propriétés simples :

 class PropertyExample {

private:

	int Fx, Fy;

	float Fcells[100][100];

protected:
	int readX() {
		return (Fx);
	}

	void writeX(int newFx) {
		Fx = newFx;
	}

	double computeZ() {

		// Do some computation and return a floating-point value...

		return (0.0);

	}

	float cellValue(int row, int col) {
		return (Fcells[row][col]);
	}

public:
	__property int X = {read = readX, write = writeX};
	__property int Y = {read = Fy};
	__property double Z = {read = computeZ};
	__property float Cells[int row][int col] = {read = cellValue};

};

Cet exemple montre plusieurs déclarations de propriétés.

  • La propriété X a un accès en lecture-écriture via respectivement les fonctions membres readX et writeX.
  • La propriété Y correspond directement à la variable membre Fy, et elle est en lecture seule.
  • La propriété Z est une valeur en lecture seule qui est calculée, elle n'est pas stockée dans un membre de donnée de la classe.
  • La propriété Cells illustre une propriété tableau avec deux indices. L'exemple suivant illustre la manière d'accéder à ces propriétés dans votre code :
// Previous code goes here
int main(int argc, char * argv[]) {
 PropertyExample myPropertyExample ;
 myPropertyExample.X = 42; // Evaluates to: myPropertyExample.WriteX(42) ;
 int  myVal1 = myPropertyExample.Y; // Evaluates to: myVal1 = myPropertyExample.Fy ;
 double  myVal2 = myPropertyExample.Z; // Evaluates to: myVal2 = myPropertyExample.ComputeZ() ;
 float  cellVal = myPropertyExample.Cells[3][7]; // Evaluates to : cellVal = myPropertyExample.cellValue(3,7);
}

Les propriétés ont beaucoup d'autres variations et fonctionnalités non présentées dans cet exemple.

Les propriétés peuvent aussi :

  • Associer la même méthode de lecture ou d'écriture à plusieurs propriétés, en utilisant l'attribut index.
  • Avoir des valeurs par défaut.
  • Etre stockées dans un fichier fiche.
  • Etendre une propriété définie dans une classe de base.


__published

Le mot clé __published spécifie que les propriétés de cette section sont affichées dans l'inspecteur d'objets, si la classe est sur la palette des composants. Seules les classes dérivées de TObject peuvent avoir des sections __published.

Les règles de visibilité des membres publiés sont identiques à celles des membres publics. Il n'y a qu'une seule différence entre les membres publics et les membres publiés : les informations de type à l'exécution (RTTI) Pascal Objet sont générées pour les membres de données et les propriétés déclarées dans une section __published. RTTI permet à une application d'interroger dynamiquement les membres de données, les fonctions membres et les propriétés d'un type de classe qui sinon serait inconnu.

Remarque : Les propriétés, les membres de données Pascal intrinsèques ou dérivés des bibliothèques, les fonctions membres et les closures sont autorisés dans une section __published. Les champs définis dans une section __published doivent être d'un type de classe. Les propriétés définies dans une section __published ne peuvent pas être des propriétés tableau. Le type d'une propriété définie dans une section __published doit être un type ordinal, réel, chaîne, ensemble court, classe ou pointeur de méthode.

Voir aussi