Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Constructor erzeugt keine Instanz

0 views
Skip to first unread message

Christian Geyer

unread,
May 27, 2003, 3:25:57 PM5/27/03
to
Hallo alle zusammen,

in einem Constructor wird keine Instanz erzeugt.

type MyClass=class
constructor Create;
public
a: integer;
end;

constructor MyClass.Create;
begin
inherited;
Assert(Self<>nil);
a:=0;
end;

Die Assertation schlägt bei einer meiner Klassen fehl.
Es wird keine Exception geworfen, erst wenn ich einen Wert auf a zuweisen
möchte (EAccessViolation).

Was kann der Grund sein, dass eine Klasse nicht erzeugt werden kann?
Speichermangel wird es auch nicht sein, da ich nur 6 Variablen habe. Es
existiert auch nur diese eine Constructor. Die Klasse funktioniert in einem
anderen Projekt.

Christian Geyer


Robert Wehofer

unread,
May 27, 2003, 3:27:07 PM5/27/03
to
Hallo Christian!

Der Konstruktor muss public sein.

Schreibe:
type MyClass=class
private
a: Integer;
public
constructor Create;
end;

LG, Robert


"Christian Geyer" <geyers.r...@web.de> wrote in message
news:bb0dsm$4ggfq$1...@ID-67212.news.dfncis.de...

Christian Geyer

unread,
May 27, 2003, 3:47:41 PM5/27/03
to
Hallo Robert,

> Schreibe:
> type MyClass=class
> private
> a: Integer;
> public
> constructor Create;
> end;

Der Constructor ist Public, daran liegt es leider nicht.
Trotzdem danke!

Christian Geyer
--
www.crispy-cow.de


Immo Landwerth

unread,
May 27, 2003, 4:13:13 PM5/27/03
to
Robert Wehofer wrote:

> Der Konstruktor muss public sein.

Muss er nicht. Es gibt durchaus auch private Konstruktoren.

--
XP Pro SP1 - D5 Pro UP 1 - XanaNews 1.15.1.2

Immo Landwerth

unread,
May 27, 2003, 4:16:03 PM5/27/03
to
Christian Geyer wrote:

> Hallo alle zusammen,
>
> in einem Constructor wird keine Instanz erzeugt.
>
> type MyClass=class
> constructor Create;
> public
> a: integer;
> end;
>
> constructor MyClass.Create;
> begin
> inherited;
> Assert(Self<>nil);
> a:=0;
> end;

Eine Sache vorab: Typen, vor allem Klassen, werden eigentlich mit einem
T als ersten Buchstaben kenntlich gemacht, z.B.

type TMyClass = class

Nun zu Deinem Problem.

Du hast vermutlich diesen Fehler gemacht:

var
a: TMyClass;
begin
a := a.Create;

//...
end;

Richtig wäre es so:

var
a: TMyClass;
begin
a := TMyClass.Create;

//...
end;

Christian Geyer

unread,
May 27, 2003, 4:57:01 PM5/27/03
to
Hallo Immo!

> Du hast vermutlich diesen Fehler gemacht:
>
> var
> a: TMyClass;
> begin
> a := a.Create;
>
> //...
> end;
>
> Richtig wäre es so:
>
> var
> a: TMyClass;
> begin
> a := TMyClass.Create;
>
> //...
> end;

Genau das war das Problem. Ich verwende auch die Schreibweise tMyClass (wie
du empfohlen hast), aber die Variable, die die erzeugte Instanz von tMyClass
hielt, hieß MyClass, und da habe ich das fehlende "t" gar nicht bemerkt :-/

Vielen Dank beim Entfernen der Tomaten auf meinen Augen! ;-)

Christian Geyer
--
www.crispy-cow.de


Robert Wehofer

unread,
May 28, 2003, 5:43:43 AM5/28/03
to

> Muss er nicht. Es gibt durchaus auch private Konstruktoren.

Schon! Aber in seinem Fall wäre es ratsam, den Konstruktor public zu machen.
Man sollte das auch in Code kennzeichnen.

Robert


Immo Landwerth

unread,
May 28, 2003, 5:58:54 AM5/28/03
to
Robert Wehofer wrote:

Das ist zwar konzeptionell sicherlich der besser Weg, löst aber wie
gesagt nicht sein Problem.

Deine Antwort klang so, als müssten Konstruktoren grundsätzlich Public
sein, damit sie eine Instanz erzeugen können. Das ist aber falsch. ;)

Robert Wehofer

unread,
May 28, 2003, 6:13:37 AM5/28/03
to
> Deine Antwort klang so, als müssten Konstruktoren grundsätzlich Public
> sein, damit sie eine Instanz erzeugen können. Das ist aber falsch. ;)

Du wirst es kaum glauben, aber ich hab schon mal protected Konstruktoren
verwendet. Private Konstruktoren sind aber fad ... ;-)

Robert


Marian Aldenhoevel

unread,
May 28, 2003, 6:41:44 AM5/28/03
to
Hi,

>>Der Konstruktor muss public sein.
>
>
> Muss er nicht. Es gibt durchaus auch private Konstruktoren.

Nur nutzt ein privates Create() in Object-Pascal nichts, weil schon
TObject.Create() public ist.

Ciao, MM

Marian Aldenhoevel

unread,
May 28, 2003, 6:44:31 AM5/28/03
to
Hi,

> Die Assertation schlägt bei einer meiner Klassen fehl.

Zeig, wie Du sie aufrufst. Leider akzeptiert Delphi auch den Aufruf des
Konstruktors als "normale" Methode und dann wird natürlich die
zusätzliche Zauberei des Konstruktoraufrufs nicht erledigt. Also nicht

var MyClass:TMyClass;

MyClass.Create;

sondern

MyClass:=TMyClass.Create;

Das ist ein typischer Fehler :-).

Ciao, MM

Jürgen Krämer

unread,
May 28, 2003, 7:19:27 AM5/28/03
to

Hallo,

Marian Aldenhoevel schrieb:


>
> Nur nutzt ein privates Create() in Object-Pascal nichts, weil schon
> TObject.Create() public ist.

er ist sogar gefährlich, weil er von Nachfahren dieser Klasse, solange
sie nicht in der selben Unit deklariert sind, nie aufgerufen werden
kann. Im Beispiel unten werden dadurch zwei unvollständige Objekte
erzeugt:

================
TestClasses.pas:
================

unit TestClasses;

interface

type
TBaseClass = class(TObject)
public
constructor Create;
end;

TDerivedClass = class(TBaseClass)
private
constructor Create;
end;

implementation

constructor TBaseClass.Create;
begin
inherited Create;

writeln('TBaseClass.Create');
end;

constructor TDerivedClass.Create;
begin
inherited Create;

writeln('TDerivedClass.Create');
end;

end.

=========
Test.dpr:
=========

{$APPTYPE CONSOLE}

program Test;

uses
TestClasses;

type
TMoreDerivedClass = class(TDerivedClass)
public
constructor Create;
end;

constructor TMoreDerivedClass.Create;
begin
inherited Create;

writeln('TMoreDerivedClass.Create');
end;

var
Derived: TDerivedClass;
MoreDerived: TMoreDerivedClass;
begin
Derived := TDerivedClass.Create;
Derived.Free;
Writeln('-----');
MoreDerived := TMoreDerivedClass.Create;
MoreDerived.Free;
end.


Die Ausgabe ist

TBaseClass.Create
-----
TBaseClass.Create
TMoreDerivedClass.Create

d.h. eventuelle Initialisierungen durch TDerivedClass.Create würden
nicht durchgeführt.

Schon

TMyClass = class(TObject)
private
constructor Create;
end;

zeigt bei Verwendung der Klasse TMyClass in einer anderen Unit das
gleiche Problem. Ich würde mir wünschen, daß Delphi bei Konstruktoren
den gleichen Hinweis wie bei überschriebenen Methoden geben und die
geringere Sichtbarkeit ignorieren würden.

Maat et joot,
Jürgen

--
Sometimes I think the surest sign that intelligent life exists elsewhere
in the universe is that none of it has tried to contact us. (Calvin)

Marian Aldenhoevel

unread,
May 28, 2003, 7:49:12 AM5/28/03
to
Hi,

> er ist sogar gefährlich, weil er von Nachfahren dieser Klasse, solange
> sie nicht in der selben Unit deklariert sind, nie aufgerufen werden
> kann.

Das ist übrigens meiner Meinung nach ein Compiler-Bug.

Denn es steht geschrieben, daß das nachträgliche Herunterstufen einer
Methode, und da sind Konstruktoren nicht ausgenommen, in Object
Pascal ohne Effekt bleibt. Schlimmstenfalls löst es einen
Compiler-Hinweis (keine Warnung) aus.

> Ich würde mir wünschen, daß Delphi bei Konstruktoren
> den gleichen Hinweis wie bei überschriebenen Methoden geben und die
> geringere Sichtbarkeit ignorieren würden.

Delphi 4 tut das denke ich. Aber es zeigt auch bei normalen Methoden
denselben Fehler. Man kann sogar mit inherited die falsche Methode
aufrufen - wenn sie "zwischendurch" in Vorgängern privat ist, werden
die Implementationen dort übersprungen.

Ich nenne auch das einen Bug.

Ciao, MM

Immo Landwerth

unread,
May 28, 2003, 8:10:18 AM5/28/03
to
Robert Wehofer wrote:

Warum?

Immo Landwerth

unread,
May 28, 2003, 8:16:35 AM5/28/03
to
Marian Aldenhoevel wrote:

Ja, aber was nützt es Dir, solange es nicht virtual ist? Selbst mit
TypeCasting kannst Du keine Instanz erzeugen.

Private Konstruktoren sollen lediglich ein Indikator dafür sein, dass
man diese Klasse nicht selbst instanzieren soll/braucht (siehe
Singleton).

Einen wirklichen Schutz im Sinne von Datensicherheit bieten die
Sichtbarkeitsbereiche allemal nicht.

Jens Winter

unread,
May 28, 2003, 8:30:54 AM5/28/03
to
Immo Landwerth wrote:

> Private Konstruktoren sollen lediglich ein Indikator dafür sein, dass
> man diese Klasse nicht selbst instanzieren soll/braucht (siehe
> Singleton).

Der Vollständigkeit halber:

Man kann die Klassen nicht mit _privaten_ Konstruktoren "von außen"
instanzieren. Da aber jede Klasse mindestens einen public Konstruktor
besitzt, nämlich "Create", kann jede Klasse instanziert werden.
Verhindern kann man dies nur durch das werfen einer Exception in "Create".
Das wird sehr schön in der Modelmaker-Version des Singleton-Musters
demonstriert:

TSingleton = class (TObject)
protected
constructor CreateInstance;
class function AccessInstance(Request: Integer): TSingleton;
public
constructor Create;
destructor Destroy; override;
class function Instance: TSingleton;
class procedure ReleaseInstance;
end;

constructor TSingleton.Create;
begin
inherited Create;
raise Exception.CreateFmt('Access class %s through Instance only',
[ClassName]);
end;

constructor TSingleton.CreateInstance;
begin
inherited Create;
end;

destructor TSingleton.Destroy;
begin
if AccessInstance(0) = Self then AccessInstance(2);
inherited Destroy;
end;

class function TSingleton.AccessInstance(Request: Integer): TSingleton;
const FInstance: TSingleton = nil;
begin
case Request of
0 : ;
1 : if not Assigned(FInstance) then FInstance := CreateInstance;
2 : FInstance := nil;
else
raise Exception.CreateFmt('Illegal request %d in AccessInstance',
[Request]);
end;
Result := FInstance;
end;

class function TSingleton.Instance: TSingleton;
begin
Result := AccessInstance(1);
end;

class procedure TSingleton.ReleaseInstance;
begin
AccessInstance(0).Free;
end;

Hier ist der Nutzer gezwungen eine Instanz von TSingleton mittels
"TSingleton.Instance" anzufordern. "Singleton := TSingleton.Create;"
wird dagegen fehlschlagen.

Ciao

--
Jens Winter SelectLine Software GmbH
www.selectline.de

Marian Aldenhoevel

unread,
May 28, 2003, 9:00:38 AM5/28/03
to
Hi,

>>Nur nutzt ein privates Create() in Object-Pascal nichts, weil schon
>>TObject.Create() public ist.
>
> Ja, aber was nützt es Dir, solange es nicht virtual ist? Selbst mit
> TypeCasting kannst Du keine Instanz erzeugen.

Ich sehe nicht, was das damit zu tun hat, ob create virtuell ist
oder nicht.

Mein Punkt war, daß es eigentlich keine Möglichkeit gibt, den
Konstruktor namens Create privat zu machen. Denn er ist bereits in
TObject als public eingeführt und alle mit class deklarierten Klassen
stammen von TObject ab.

Man kann Create in einem Nachkommen nun zwar als private überschreiben,
man erreicht damit aber nicht, daß man keine Instanz der Klasse
erzeugen kann. Man erreicht eins von zwei Dingen:

- Einen Compiler-Hinweis, daß die Sichtbarkeit verringert wurde,
was aber keinen Unterschied für die Funktion macht. Das wäre
das dokumentierte Verhalten.

- Einen potentiell sehr schwer zu findenden Fehler weil der
überschriebene Konstruktor einfach nicht aufgerufen und die
Instanz damit nur halb initialisiert wird. Das ist das
fehlerhafte Verhalten, das D4 zeigt.

Bei der ersten Version ist die private-Deklaration von Create() ein
reines Ärgernis, denn der zusätzliche Hinweis kommt unabhängig davon,
ob ein Client versucht Create() aufzurufen.

Bei der zweiten Version ist die private-Deklaration Quelle eines
obskuren Laufzeitfehlers. Sicher nicht im Sinne des Erfinders.

> Private Konstruktoren sollen lediglich ein Indikator dafür sein, dass
> man diese Klasse nicht selbst instanzieren soll/braucht (siehe
> Singleton).

Ja. Aber doch nicht als Placebo.

Besser man lässt Create öffentlich und lässt es entweder in jedem
Fall eine Exception werfen (wenn man einen zweiten privaten Konstruktor
hat, den man für die Erzeugung des Singletons verwenden kann), oder
nur unter bestimmten Bedingungen:

constructor TSingleton.Create;
begin
if _Singleton<>NIL then raise ESingletonError.Create('es gibt
schon eine Instanz von TSingleton');
inherited Create;
end;

Das liefert weder einen unsinnigen Hinweis noch einen unklaren Fehler.

> Einen wirklichen Schutz im Sinne von Datensicherheit bieten die
> Sichtbarkeitsbereiche allemal nicht.

Einverstanden. Aber man muss Arbeit investieren wenn man einen
Sichtbarkeitsschutz aufknacken will. Bei einem privaten Create()
ist das nicht der Fall.

Also: Private Konstruktoren ja, privates Create() nein. In Object
Pascal, wie wir es kennen, versteht sich.

Ciao, MM

Immo Landwerth

unread,
May 28, 2003, 9:46:16 AM5/28/03
to
Marian Aldenhoevel wrote:

> Hi,
>
> >>Nur nutzt ein privates Create() in Object-Pascal nichts, weil schon
> >>TObject.Create() public ist.
> > > Ja, aber was nützt es Dir, solange es nicht virtual ist? Selbst
> > > mit
> > TypeCasting kannst Du keine Instanz erzeugen.
>
> Ich sehe nicht, was das damit zu tun hat, ob create virtuell ist
> oder nicht.

Nun, wenn Create virtuell wäre, ließe sich in jedem Fall die richtige
Version aufrufen.

> Mein Punkt war, daß es eigentlich keine Möglichkeit gibt, den
> Konstruktor namens Create privat zu machen. Denn er ist bereits in
> TObject als public eingeführt und alle mit class deklarierten Klassen
> stammen von TObject ab.

Point Taken. Ich ging davon aus, dass man Create dann nicht mehr
verwenden kann.

> Besser man lässt Create öffentlich und lässt es entweder in jedem
> Fall eine Exception werfen (wenn man einen zweiten privaten
> Konstruktor hat, den man für die Erzeugung des Singletons verwenden
> kann), oder nur unter bestimmten Bedingungen:

ACK.

> Also: Private Konstruktoren ja, privates Create() nein. In Object
> Pascal, wie wir es kennen, versteht sich.

Jep. Jetzt habe ich es (hoffentlich) endgültig verstanden. :)

Marian Aldenhoevel

unread,
May 28, 2003, 11:22:25 AM5/28/03
to
Hi,

> Nun, wenn Create virtuell wäre, ließe sich in jedem Fall die richtige
> Version aufrufen.

Wegen des genannten Bugs ist nicht einmal das garantiert :-/.

Ciao, MM

Immo Landwerth

unread,
May 28, 2003, 11:37:07 AM5/28/03
to
Marian Aldenhoevel wrote:

Wieso? ich dachte, das würde nur nicht virtuelle Methoden betreffen?

Worin genau liegt denn der Bug?

Marian Aldenhoevel

unread,
May 28, 2003, 12:08:26 PM5/28/03
to
Hi,

> Worin genau liegt denn der Bug?

Unit A;

interface

type TBase=class(TObject)
public
procedure Buh; virtual;
end;

implementation

procedure TBase.Buh;
begin
writeln('TBase.Buh');
end;

end.


Unit B;

interface

uses A

type TDerived=class(TBase)
private
procedure Buh; override;
end;

implementation

procedure TDerived.Buh;
begin
inherited;
writeln('TDerived.Buh');
end;

end.


Unit C;

interface

uses B

type TDerivedDerived=class(TDerived)
public
procedure Buh; override;
end;

implementation

procedure TDerivedDerived.Buh;
begin
inherited;
writeln('TDerivedDerived.Buh');
end;

end.


begin
TBase.Create.Buh;
writeln('*****');
TDerived.Create.Buh;
writeln('*****');
TDerivedDerived.Create.Buh;
end.


Das Herunterstufen in der Sichtbarkeit in Derived sollte keine
Auswirkung auf das Programm haben. Weil Buh schon in TBase public
ist, bleibt es public. Wie man sieht ist es ja auch möglich
TDerived.Buh von außen aufzurufen. Aber es kommt das falsche
dabei heraus.

Der Compiler äußert einen Hinweis. Das darf er nur, wenn die vom
Hinweis implizierte Handlung die Funktion des Programms nicht
ändert.

Bei Hinweisen sind das immer triviale Dinge, wie eine unnötige
Zuweisung weglassen oder eine Variablendeklaration streichen.
Hier wäre es TDerived.Buh ebenfalls public zu machen, denn das
ist es ohnehin wegen der Vorgabe in TBase. Tut man das, kommt
aber ein anderes Programm heraus.

Ich habe dieses Beispiel jetzt nicht übersetzt, bin aber in
einem großen D4-Projekt von jemand Drittem darüber gestolpert.
Der hat das als Feature verwendet um in bestimmten Klassen
innerhalb einer Hierarchie Funktionalität auszusetzen.

Ich habe den Hinweis befolgt und das Programm dabei
kaputtrepariert...

Ciao, MM

Jens Winter

unread,
May 27, 2003, 3:52:15 PM5/27/03
to
Christian Geyer wrote:

> Der Constructor ist Public, daran liegt es leider nicht.

Genauer gesagt published.
Kontrolliere, ob in dem betreffenden Konstruktor auch wirklich das
inherited vorhanden ist.

Ciao

Jens Winter

Michael Winter

unread,
Jun 2, 2003, 3:09:51 PM6/2/03
to
Jens Winter wrote:

>> Der Constructor ist Public, daran liegt es leider nicht.
>
> Genauer gesagt published.

Weil wir nicht den kompletten Quelltext kennen, wissen wir es nicht
genau. Aber die Gewinnchancen sind bei einem Tipp auf 'public' deutlich
höher.

-Michael

Jens Winter

unread,
Jun 3, 2003, 1:46:42 AM6/3/03
to
Michael Winter wrote:

>>Genauer gesagt published.
>
>
> Weil wir nicht den kompletten Quelltext kennen, wissen wir es nicht
> genau. Aber die Gewinnchancen sind bei einem Tipp auf 'public' deutlich
> höher.

Nunja. Wenn man sich den Quelltext von Christian anschaut, braucht man
nicht raten. Er lautete:

type MyClass=class
constructor Create;
public
a: integer;
end;


Ciao

--
Jens Winter SelectLine Software GmbH
www.selectline.de

"Terrorismus ist der Krieg der Armen und Krieg ist der Terrorismus der
Reichen." (Sir Peter Ustinov)

Michael Winter

unread,
Jun 3, 2003, 4:38:35 PM6/3/03
to
Jens Winter wrote:

> Michael Winter wrote:

>> Weil wir nicht den kompletten Quelltext kennen, wissen wir es
>> nicht genau. Aber die Gewinnchancen sind bei einem Tipp auf
>> 'public' deutlich höher.
>
> Nunja. Wenn man sich den Quelltext von Christian anschaut, braucht
> man nicht raten. Er lautete:
>
> type MyClass=class
> constructor Create;

Eben. Wirf mal eine Blick auf das Thema 'Sichtbarkeit von
Klassenelementen' in der OH.

-Michael, doch lieber gewettet haben wollend

Jens Winter

unread,
Jun 4, 2003, 2:05:02 AM6/4/03
to
Michael Winter wrote:

>>Nunja. Wenn man sich den Quelltext von Christian anschaut, braucht
>>man nicht raten. Er lautete:
>>
>>type MyClass=class
>> constructor Create;
>
>
> Eben. Wirf mal eine Blick auf das Thema 'Sichtbarkeit von
> Klassenelementen' in der OH.

Ich darf mal zitieren:
"Die Elemente am Anfang einer Klassendeklaration ohne explizite
Sichtbarkeitsangabe werden standardmäßig als *published* deklariert, [...]"

Leider hatte ich nie Das Kleingedruckte[tm] gelesen:
"[...]wenn die Klasse im Status *{$M+}* compiliert oder von einer mit
*{$M+}* compilierten Klasse abgeleitet wurde. Andernfalls erhalten sie
das Attribut *public*."

Wieder was dazugelernt. Danke. Zum Glück haben wir nicht gewettet. ;-)

0 new messages