2010-12-15 60 views
4

j'ai une classe de base declarated comme celui-ciavec fonction nommée créer dans delphi

type 
    TBaseClass=class 
    protected 
    constructor Create(LoadData:boolean;const Param1,Param2:string); overload; 
    public 
    Destructor Destroy; override; 
    end; 

maintenant dans une autre unité une classe enfant TChid_Class qui descendent de TBaseClass

TChid_Class=class(TBaseClass) 
    function Create(const Param1, Param2 : String;const Param3 : OleVariant ; var Param4 : LongInt): Integer;overload; 
    constructor Create; overload; 
    constructor Create(LoadData:boolean); overload; 
    end; 

dans cette classe existe une fonction appelé Create comme les constructeurs, le problème est, quand j'essaie de créer une instance à la TChid_Class j'ai une violation d'accès.

i a écrit cette petite application console qui montre le problème

program TestClass; 

{$APPTYPE CONSOLE} 

uses 
    Variants, 
    SysUtils; 

type 
    TBaseClass=class 
    protected 
    constructor Create(LoadData:boolean;const Param1,Param2:string); overload; 
    public 
    Destructor Destroy; override; 
    end; 

    TChid_Class=class(TBaseClass) 
    function Create(const Param1, Param2 : String;const Param3 : OleVariant ; var Param4 : LongInt): Integer;overload; 
    constructor Create; overload; 
    constructor Create(LoadData:boolean); overload; 
    end; 

{ TBaseClass } 

constructor TBaseClass.Create(LoadData: boolean; const Param1, Param2: string); 
begin 
    inherited Create; 
    Writeln('constructor TBaseClass.Create(LoadData: boolean; const Param1, Param2: string);'); 
end; 

destructor TBaseClass.Destroy; 
begin 
    //Code 
    inherited; 
end; 

{ TChid_Class } 

function TChid_Class.Create(const Param1, Param2: String; const Param3: OleVariant; var Param4: Integer): Integer; 
begin 
    Writeln('function create'); 
    Result:=0; 
end; 

constructor TChid_Class.Create; 
begin 
    Writeln('constructor TChid_Class.Create'); 
    Create(True); 
end; 

constructor TChid_Class.Create(LoadData: boolean); 
begin 
    Writeln('constructor TChid_Class.Create(LoadData: boolean)'); //here is the access violation 
    Create(LoadData,'Value 1','Value 2'); 
end; 


var 
    Invoker : TChid_Class; 
    Pid  : integer; 
begin 
    try 
    Invoker:=TChid_Class.Create; 
    try 
     Invoker.Create('','',Unassigned,Pid) 
    finally 
     Invoker.Free; 
    end; 
    except 
    on E:Exception do 
     Writeln(E.Classname, ': ', E.Message); 
    end; 

    readln; 
end. 

si je renomme la fonction créer, le problème est d'aller loin, mais je suis à la recherche d'une solution sans renommer la fonction de création ou les constructeurs.

utilisant delphi 2007

Merci à l'avance.

+0

Je ne suis pas une violation d'accès lorsque je lance cette sous Delphi 2010. Quelle version utilisez-vous? –

+0

@Mason - Je reçois l'AV avec D2007. @Salvador - Déplacer la fonction pour après les constructeurs semble éviter l'AV. –

+0

J'utilise delphi 2007 – Salvador

Répondre

3

Avec Delphi 7, j'ai aussi un AV. Renommer la méthode ou la déplacer (comme suggéré par Sertac Akyuz) corrige l'AV.

Ce que je découvre en regardant l'assembleur:

Lors de la création d'un nouvel objet une valeur non nulle est placée dans le registre dl avant d'appeler le constructeur.

mov dl,$01    // dl set to 1 
mov eax,[$00401268] 
call TChild_Class.Create 

Puis dans le constructeur, sur la ligne begin, ClassCreate est appelée lorsque dl est non nul.

test dl,dl  
jz +$08   //if dl = 0 then do not call ClassCreate 
add esp,-$10 
call -$00000396 //calls ClassCreate 

Mais ça ne va pas, avec votre code, le compilateur définit dl-1 avant d'appeler à nouveau Create(True), donc sur la ligne de beginTChid_Class.Create(LoadData: boolean);, ClassCreate est appelé à nouveau qui se traduit par un AV.

Après avoir renommé votre fonction ou de déplacer sa déclaration, le compilateur efface dl (xor edx,edx) au lieu de le mettre à 1, avant d'appeler Create(True).Je pense que c'est un bogue dans le compilateur Delphi qui est corrigé dans Delphi 2010 et supérieur.

+0

Très proche, mais quand j'ai vérifié avec debbuger, le compteur de référence ** dl ** est toujours 1, mais l'adresse VMT (table de méthode virtuelle) dans ** eax ** est fausse dans le second constructeur quand il appelle TObject.NewInstance! –

+0

@GJ: De toute façon, je pense que c'est un bug dans le compilateur Delphi, pas le code. –

2

réponse partielle: Dans votre constructeur TChid_Class.Create (LoadData: Boolean) vous manque 'hérité' dans votre Créer appel. Cela corrige un AV dans l'exemple de code si vous appelez * TChid_Class.Create (True) * dans votre projet de démonstration.

constructor TChid_Class.Create(LoadData: boolean); 
begin 
    Writeln('constructor TChid_Class.Create(LoadData: boolean)'); //here is the access violation 
    inherited Create(LoadData,'Value 1','Value 2'); //add "INHERITED" 
end; 
+0

Merci, j'ai essayé votre suggestion, mais l'AV continue, essayez-vous cela dans Delphi 2007? – Salvador

+0

avez-vous ajouté hérité à tous les TChid_Class.Create? –

+0

Oui, j'essaie ça. – Salvador

1

Vous utilisez un chemin incorrect pour créer l'objet! Vous ne pouvez pas appeler plus d'un constructeur dans une classe à la création, mais vous pouvez créer uniquement la création. donc le droit chemin est quelque chose comme:

constructor TChid_Class.Create(LoadData: boolean = True); 
begin 
    Writeln('constructor TChid_Class.Create(LoadData: boolean)'); //here is the access violation 
    inherited Create(LoadData,'Value 1','Value 2'); 
end; 

mot réservé hérité dire au constructeur de parent objet est créé dans le constructeur de la classe des enfants. Vérifié sous D2007!

+0

Il n'y a rien de mal à appeler plusieurs constructeurs de la même classe. –

+0

@The_Fox: Oui c'est! Vous ne pouvez pas appeler plus d'un constructeur dans la même classe, mais vous ne pouvez l'hériter que de la classe parent! Parce que lorsque vous appelez le second constructeur dans la classe some, le TObject.NewInstance sera appelé à nouveau et cela provoquera une exception! J'ai vérifié cette erreur avec debbuger sous D2007. –

+0

Ensuite, c'est probablement un bug (voir ma réponse), car il n'y a rien de mal à avoir plusieurs constructeurs surchargés qui appellent tous un constructeur avec des paramètres par défaut. Juste comme Salvador l'a fait. –

0

Cela a été un bogue du compilateur jusqu'à la dernière mise à jour de Delphi 2007.
Cela fonctionne à partir de Delphi 2009 et plus. Renommer la fonction Create pour Initialize résout votre problème. Il est non seulement une solution de contournement, , mais est correct à la fois pour la correction du bogue et du point de vue du codage:
En règle générale, il doit y avoir une distinction claire dans la construction et l'initialisation des instances d'objet.

  • La construction doit être courte et ne pas déclencher d'exceptions.
  • L'initialisation peut prendre plus de temps et déclencher une exception.

--jeroen