Je suis en ce moment, en train de concevoir une DLL qui echange des objets avec son programme via des Interfaces, j'essaye de rester propres !
Quand je parle d'interface, je parle de classe abstraite pure !
Comme IFirstInterface

Même en Delphi, je n'ai jamais trop poussé cela, Je fais des ActiveX habituellement !
Là, c'est une DLL normale (pour faciliter son déploiement) avec une fonction exportée qui renvoi un pointeur vers une Interface racine puis à partir de ce moment, tout est objet\interface !

J'aurais plusieurs DLL avec la même interface, l'implémentation changera en fonction du matériel piloté !
J'ai commencé par une DLL de démonstration pour construire mon code !
Je viens de séparer l'implémentation en deux classes, une première qui sera réutilisable (code commun que je peux factoriser), puis une seconde qui sera totalement spécifique

Lors de la séparation, j'ai eu énormément de problème !
Au début, lors de mes lectures sur l'utilisation d'interface (classe abstraite pure) en C++ Builder, j'ai compris avec ce sujet, que le support était partiel (surement lié au limite de l'héritage multiple et que la notion d'interface n'existe pas vraiment en C++, cela doit faire paniquer la VMT du TObject)
Mon modèle tel que je le prévoyais n'aurait pas ce type d'architecture, je ne me suis pas inquiété !
Cela cache en fait un conflit de la virtualisation des méthodes !
Effectivement, si l'on prend l'exemple fourni, l'implémentation B de CFirstImplPhase2, n'est pas accessible, l'interface reste sans implementation, le compilateur laisse passer son rien voir !
A l'execution, ça plante (pas une exception, ça plante bien sévère !)

J'ai consulté le tutoriel
Technique : Travailler avec les Interfaces en C++Builder Partie 1
Cela ne me plaisait pas de partir sur IInterface !
Est-ce obligatoire si l'objet est de type VCL ?
Demain, si j'ai le courage, je passe mes interfaces C++ en IInterface, pour voir si mon bug d'implémentation dans deux classes héritées disparait !

le bug que je souhaite corriger ne concerne que IFirstInterface, TFirstImpl et TFirstImplPhase2

ISecondInterface et TSecondImpl, c'est un autre bug, surement lié au premier !

le Tutoriel de DjmSoftware lui concerne TSecondBisImpl, ce qui fonctionne parfaitement !

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
class IFirstInterface
{
public:
  virtual int A() = 0;
  virtual int B() = 0;
};
 
class ISecondInterface
{
public:
  virtual int C() = 0;
  virtual int D() = 0;
};
 
class CFirstImpl: public IFirstInterface
{
public:
  int A() {return -1;}
};
 
class CFirstImplPhase2: public CFirstImpl
{
public:
  int B() {return -22;}
};
 
class CSecondImpl: public CFirstImplPhase2, public ISecondInterface
{
public:
  int C() {return -333;}
  int D() {return -4444;}
};
 
class TFirstImpl: public TObject, public IFirstInterface
{
public:
  int A() {return -1;}
};
 
class TFirstImplPhase2: public TFirstImpl
{
public:
  int B() {return -22;}
};
 
class TSecondImpl: public TFirstImplPhase2, public ISecondInterface
{
public:
  int C() {return -333;}
  int D() {return -4444;}
};
 
class TSecondBisImpl: public IFirstInterface, public ISecondInterface
{
public:
  int A() {return -1;}
  int B() {return -22;}
  int C() {return -333;}
  int D() {return -4444;}
};
 
 
 
//---------------------------------------------------------------------------
void __fastcall TLanguageBasicsForm::BtnInterfaceSeparateImplementsClick(TObject *Sender)
{
  IFirstInterface* ObjC = new CFirstImplPhase2();
  OutputDebugString(Format("A : %d - B : %d", ARRAYOFCONST((ObjC->A(), ObjC->B()))).c_str());
  delete ObjC;
 
  IFirstInterface* ObjVCL = new TFirstImplPhase2();
  OutputDebugString(Format("A : %d", ARRAYOFCONST((ObjVCL->A()))).c_str());
  // B() plantera : Pure virtual function called
  // Le programme sera probablement totalement dans les choux (CTRL + F2 pour quitter)
  // OutputDebugString(Format("B : %d", ARRAYOFCONST((ObjVCL->B()))).c_str());
  delete (TFirstImplPhase2*)ObjVCL;
 
  CSecondImpl* ObjC2 = new CSecondImpl();
  IFirstInterface* IC = ObjC2;
  ISecondInterface* IC2 = ObjC2;
  OutputDebugString(Format("A : %d - B : %d - C : %d - D : %d", ARRAYOFCONST((IC->A(), IC->B(), IC2->C(), IC2->D()))).c_str());
  delete ObjC2;
 
  TSecondImpl* ObjVCL2 = new TSecondImpl();
  IFirstInterface* IVCL = ObjVCL2;
  ISecondInterface* IVCL2 = ObjVCL2;
  OutputDebugString(Format("A : %d", ARRAYOFCONST((IVCL->A()))).c_str());
  // OutputDebugString(Format("B : %d", ARRAYOFCONST((IVCL->B()))).c_str());
  OutputDebugString(Format("C : %d", ARRAYOFCONST((IVCL2->C()))).c_str());
  OutputDebugString(Format("D : %d", ARRAYOFCONST((IVCL2->D()))).c_str());
  delete ObjVCL2;
 
  TSecondBisImpl* ObjBis = new TSecondBisImpl();
  IFirstInterface* IBis = ObjBis;
  ISecondInterface* IBis2 = ObjBis;
  OutputDebugString(Format("A : %d - B : %d - C : %d - D : %d", ARRAYOFCONST((IBis->A(), IBis->B(), IBis2->C(), IBis2->D()))).c_str());
  delete ObjBis;
 
}
//---------------------------------------------------------------------------
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
A : -1 - B : -22
A : -1
// Ignore B
A : -1 - B : -22 - C : -333 - D : -4444
A : -1
// Ignore B
C : -1
D : -22
A : -1 - B : -22 - C : -333 - D : -4444