Vector<class with private copy-constr/ referenc

Hi,

diesen Artikel habe ich auch in einem News-Brett gepostet, daher
ist er auf Englisch, fuer Hilfe waere ich dankbar!

I have a class with a private copy-constructor (it has a copy
constructor which needs one more arg though). There is no way to
put that into a vector, is there? So I tried to use references to
that class. But vector can neither deal with references, right?

It’s a bit complicated. The class I talk about is nested in
another class. The nested class needs a pointer to the outside,
giving me a Java like call back construct. Now I want to have a
vector of that nested type, and to keep the depending code simple
I’d like to use a vector of objects. Second option would be
vector of references. I’d hate to have to use pointers since the
whole stuff is part of the basic hirarchy for dozens of classes
that depend on it …

Any ideas? Or are vectors actually able to deal with the problem?
I could also provide a copyconstructor. But that would be unsafe.
It should then only be used by vector not by users of the
surrounding class. Again I don’t want to make the nested class
private or protected since it being public provides a nicer
interface for initializing the surrounding class.

I also thought about making vector a friend of my nested class.
That would be fine as far as I am concerned (since the actual
vector in the outside class is private), but that’s not enough.
Using my compiler (gcc 2.95.2) I have to make some template
function also a friend. Example code:

#include 

class A
{
private:
 A(const A&amp:wink: {}
public:
 A() {}
 friend vector; // I could live with this  
 friend void construct (A \*, const A &amp:wink:;  
 // that construct thing might make the code unportable  
};  
  
void main()  
{  
 vector va;  
 // vector vra; // not possible  
 va.push\_back (A () );  
}

Trouble is that construct stuff. Is that part of the STL-Standard
or of the implementation of my compiler. Thus will it be
portable? Oh damnit I just found out, that I have to make two
more friends, to make clearing the vector and other stuff work.
The friends for actual code (I am too lazy, to work it out with
the example …) would be:

friend class Plug::LinkTable::SameIdLinks \*
 \_\_copy(Plug::LinkTable::SameIdLinks \*,
 Plug::LinkTable::SameIdLinks \*,
 Plug::LinkTable::SameIdLinks \*,
 random\_access\_iterator\_tag, ptrdiff\_t \*);
friend class Plug::LinkTable::SameIdLinks \*
 \_\_copy\_backward(Plug::LinkTable::SameIdLinks \*,
 Plug::LinkTable::SameIdLinks \*,
 Plug::LinkTable::SameIdLinks \*,
 random\_access\_iterator\_tag, ptrdiff\_t \*);

Lol, that can’t be portable. So, any ideas?

Cheers

Thorsten

Hi Thorsten :smile:

I have a class with a private copy-constructor (it has a copy
constructor which needs one more arg though).

Das ist eine extrem starke Einschränkung der Verwendbarkeit deiner Klasse. Du kannst nur Referenzen und Zeiger auf solche Klassenobjekte bei Funktionsaufrufen übergeben. Und du kannst nie ein neues Klassenobjekt definieren und ihm gleichzeitig einen Wert zuweisen. So was wie

A b= c;

ist also unmöglich.

There is no way to put that into a vector, is there?

Nein, es gibt keine Möglichkeit. Aus Effizienzgründen arbeitet eigentlich jede vector-Implementation mit dem Copy-Construktor. Ansonsten müsste erst der Constructor und dann der Zuweisungsoperator aufgerufen werden.

So I tried to use references to that class. But vector can

neither deal with references, right?

Stimmt, auch das geht nicht. Referenzen sind konstante Zeiger, die automatisch derefernziert werden. Wie alle konstanten Objekte in C++, müssen daher auch Referenzen bei ihrer Definition initialisiert werden:

A &a= b; //Ist in Ordnung.

A &a; //Gibt schreckliche Haue vom Compiler
a= b;

I could also provide a copyconstructor. But that would be
unsafe.

Aber nur so geht’s! Eine Klasse ohne Copy-Constructor ist recht nutzlos und sollte eigentlich nie designt werden.

It should then only be used by vector not by users of the
surrounding class.

Dazu bräuchtest du eine friend-Deklaration für vector, so dass du den Copy-Constructor privat machen kannst, vector aber dennoch Zugriff darauf hat. Das Problem ist nur, dass du nicht weißt, welche fremde Funktionen vector selbst aufruft. Diese müssten theoretisch auch alle Freunde werden, weil sie vermutlich eine Parameterübergabe durchführen müssen und daher den Copy-Constructor aufrufen …

Alles in allem würde ich an deiner Stelle mal über das Design meiner Klasse nachdenken. „friend“ macht den objektorientierten Ansatz sowieso kapputt. Ein Copy-Constructor ist eigentlich ein Muss !!!

Viele Grüße

Stefan.

Hi,

Das ist eine extrem starke Einschränkung der Verwendbarkeit
deiner Klasse. Du kannst nur Referenzen und Zeiger auf solche
Klassenobjekte bei Funktionsaufrufen übergeben.

Naja, das ist mir schon klar, und dafuer gibt es auch gute
Gruende. Bei Funktionsaufrufen verwende ich ohnehin
ausschliesslich const referencen - schon aus Effiziensgruenden,
dahaer ist das schonmal kein Problem.

Und du kannst
nie ein neues Klassenobjekt definieren und ihm gleichzeitig
einen Wert zuweisen. So was wie

A b= c;

Ist nicht unbedingt Sache des Copyconstructors. Ich kann ja auch
den Zuweisungsoperator ueberladen, aber so oder so, ich will ja
gerade nicht, dass die Klasse einfach so kopiert wird.

Nein, es gibt keine Möglichkeit.

Stimmt, hatte ich nicht drueber nachgedacht, muss ja irgendwie
reinkommen …

Stimmt, auch das geht nicht. Referenzen sind konstante
Zeiger, die automatisch derefernziert werden. Wie alle
konstanten Objekte in C++, müssen daher auch Referenzen bei
ihrer Definition initialisiert werden:

Das ist mir wohl auch klar. Folgender Code funktioniert z.B.,
dass es mit vector nicht geht ist wohl auch eine
Effizienzgeschichte (oder Schlamperei ,-&gt:wink::

#include 

template class A {
 T t;
public:
 T readme() {return t;}
 //const T& readme; // geht nicht, weil reference auf reference
 A(T t0) : t(t0)/\*, readme(t)\*/ {}
};

void main() {
 int a(0);
 A air(a);
 cout 

Die Verwenden wohl auch irgendwo pointer oder referencs auf die
zu haltenden Objekte im Vector, sonst waere es kein Problem.



> Aber nur so geht's! Eine Klasse ohne Copy-Constructor ist  
> recht nutzlos und sollte eigentlich nie designt werden.


Das ist - sorry - totaler Quatsch. Callback-Classes lassen sich
in C++ z.B. nicht anders verwirklichen. Ueberhaupt braucht man
oft nested friend die einen Pointer nach aussen haben (wenn man
die Komplexitaet schwieriger Klassen aufloesen oder das Interface
logisch und uebersichtlich gestalten will). Und das ist
ja auch voellig ok, wenn sich die aeussere Klasse ums kopieren
kuemmert (und die dann die inneren sauber mitkopiert).



> Dazu bräuchtest du eine friend-Deklaration für vector,  
> so dass du den Copy-Constructor privat machen kannst,  
> vector aber dennoch Zugriff darauf hat. Das Problem  
> ist nur, dass du nicht weißt, welche fremde Funktionen  
> vector selbst aufruft.


Doch, das weiss ich, der Compiler ist ja kein Geheimniskraemer,
und wenn Du mein Posting weiter gelesen haettest, wuesstest Du es
auch :wink: Ergibt sich das Problem der Portabilitaet, weshalb ich
diese Moeglichkeit ausschliesse (obwohl ich es erstmal gemacht
habe um weitermachen zu koennen, bis mir eine Loesung einfaellt).



> Alles in allem würde ich an deiner Stelle mal über das Design  
> meiner Klasse nachdenken. "friend" macht den  
> objektorientierten Ansatz sowieso kapputt.


Gaehn ;-\>
Und wieder Quatsch - und wieder sorry. Gerade weil nested friends
so eine feine Sache sind hat Java - mit Sicherheit die
"objektorientiertere" Sprache - das Problem gleich in der
Sprachsyntax geloest.



> Ein Copy-Constructor ist eigentlich ein Muss !!!


Siehe oben. Und sag mir, wie Du nested friends mit
Callback-Funktion in C++ mit Standard-Copy-Constructor
implementierst. _Das_ wuerde mir helfen :wink:

Gruss

Thorsten

Hi Thorsten :smile:

Und du kannst
nie ein neues Klassenobjekt definieren und ihm gleichzeitig
einen Wert zuweisen. So was wie

A b= c;

Ist nicht unbedingt Sache des Copyconstructors.
Ich kann ja auch :den Zuweisungsoperator ueberladen …

Doch! Das ist ausschließlich Sache des Copy-Constructors. Der Zuweisungsoperator wird überhaupt nicht aufgerufen!

Aber nur so geht’s! Eine Klasse ohne Copy-Constructor ist
recht nutzlos und sollte eigentlich nie designt werden.

Das ist - sorry - totaler Quatsch.

Hmm. Dann lese mal ein paar Bücher über objektorientiertes Design. C++ ist stark auf den Copy-Constructor ausgerichtet. Das merkst du schon daran, dass du mit Klassen ohne Copy-Constructor die STL nur sehr bedingt nutzen kannst.

Dazu bräuchtest du eine friend-Deklaration für vector,
so dass du den Copy-Constructor privat machen kannst,
vector aber dennoch Zugriff darauf hat. Das Problem
ist nur, dass du nicht weißt, welche fremde Funktionen
vector selbst aufruft.

Doch, das weiss ich, der Compiler ist ja kein Geheimniskraemer,

Moment, dein Compiler ist kein Geheimniskrämer. Aber du weißt nicht, welche Funktionen andere Compiler aufrufen. Der Standard schreibt ja nicht vor, wie die STL implementiert wird, sondern nur, was sie können muss. Und das hast du ja auch richtig als dein Problem (Portierbarkeit) erkannt :smile:

und wenn Du mein Posting weiter gelesen haettest,

Wie kommst du darauf, dass ich das nicht getan habe?

Ergibt sich das Problem der Portabilitaet, weshalb ich
diese Moeglichkeit ausschliesse (obwohl ich es erstmal gemacht
habe um weitermachen zu koennen, bis mir eine Loesung
einfaellt).

s.o.

Alles in allem würde ich an deiner Stelle mal über das Design
meiner Klasse nachdenken. „friend“ macht den
objektorientierten Ansatz sowieso kapputt.

Gaehn ;->

Ich hoffe, es ist bei dir kein grundsätzliches Problem, dass du immer sehr müde bist, wenn man dir was Wichtiges sagt :smile:

Und wieder Quatsch - und wieder sorry. Gerade weil nested
friends
so eine feine Sache sind hat Java - mit Sicherheit die
„objektorientiertere“ Sprache - das Problem gleich in der
Sprachsyntax geloest.

Moment! Du kannst bei deinem Problem C++ nicht mit Java vergleichen. C++ ist eine Pseudo-OOP-Sprache, in der du mit gewissen Restriktion leben musst. Eine davon ist z.B., dass jede Klasse einen Copy-Constructor haben sollte. Nicht umsonst gibt es für jede Klasse einen Default-Copy-Constructor, wenn du keinen explizit definiert hast! C++ eignet sich daher auch nur bedingt zum Umsetzen bestimmter OOP-Strukturen.

… Und sag mir, wie Du nested friends mit
Callback-Funktion in C++ mit Standard-Copy-Constructor
implementierst. Das wuerde mir helfen :wink:

Gar nicht! Dafür ist C++ die falsche Sprache.

cu Stefan.

It’s a bit complicated. The class I talk about is nested in
another class. The nested class needs a pointer to the
outside,
giving me a Java like call back construct. Now I want to have
a
vector of that nested type, and to keep the depending code
simple

Hmm… ich habe mich auch einmal ein wenig mit diesem Problem beschaeftigt, weil auch mir der Pointersyntax nicht gefiel. Das einzige was mir dazu einfaellt ist eine InterfaceKlasse, ich habe das aber noch nicht ausprobiert.
Das waere dann eine Klasse die private von vector erbt. Alle Zugriffsfunktionen muesstest du dann ueberschreiben, aber das scheinen ja, so wie sich das bei dir anhoert nicht so viele zu sein.
In etwa so:
template vectorIF : private vector
{
// notwendige konstruktoren muessen angeboten werden, sollten inline sein, und an vector durchreichen
T operator[](int a) { return *vector::operator } // ich habe das jetzt nicht getestet, aber theoretisch muesste es funktionieren
push_back(T* a) { vector::stuck_out_tongue:ush_back(a); }
// … alle anderen funktionen die du noch brauchst, vielleicht gibt es so etwas ja auch
// schon im Netz, denn eigentlich wuerde das ja Sinn machen es einmal fuer den gesamten
// vector durchzuziehen
};

Sag mal Bescheid ob es etwas nuetzt, und wenn ja ob es funktioniert.

Gruss Ben

Hi,

A b= c;

Ist nicht unbedingt Sache des Copyconstructors.
Ich kann ja auch :den Zuweisungsoperator ueberladen …

Doch!

Oops, gepennt, schon klar.

Hmm. Dann lese mal ein paar Bücher über objektorientiertes
Design.

Ja doch. Wieviele denn noch? :wink:

und wenn Du mein Posting weiter gelesen haettest,

Wie kommst du darauf, dass ich das nicht getan habe?

Weil Du einen Vorschlag gemacht hast, den ich selbst in dem Posting schon gemacht und gleich verworfen habe.

Moment! Du kannst bei deinem Problem C++ nicht mit Java
vergleichen. C++ ist eine Pseudo-OOP-Sprache, in der du mit
gewissen Restriktion leben musst.

Nein, das ist nun wirklich falsch (dass ich mit Restriktionen leben muss). C++ ist wie C und Java eine allgemeine Programmiersprache mit der sich eine Turingmachiene Programmieren laesst. Der erste C+±"Compiler" hat den C++ Code in C uebersetzt und das Ergebnis dann kompiliert. Man kann damit alles machen. Evtl. laesst sich allerdings nicht vermeiden, dass die Interfaces (und das Design allgemein) darunter leiden. Und im Zweifelsfasll rechtfertigt der Aufwand das Ergebnis nicht.

Eine davon ist z.B., dass
jede Klasse einen Copy-Constructor haben sollte.

Das ist eine Einschraenkung, die ich in meinen Designs niemals akzeptieren wuerde. Natuerlich brauchen die meisten Klassen, zu der Client-Programmierer Zugriff haben einen Copykonstruktor. Aber im private-Bereich? Wozu gibt es denn ueberhaupt nested classes?

… Und sag mir, wie Du nested friends mit
Callback-Funktion in C++ mit Standard-Copy-Constructor
implementierst. Das wuerde mir helfen :wink:

Gar nicht! Dafür ist C++ die falsche Sprache.

Na, dafuer mache ich das aber ziemlich oft. Aber vielleicht wuerden Dir ja die Haare zu Berge stehen, wenn Du meinen Code saehest :wink:
Ich denke der Vorschlag, der hier noch gekommen ist, ist ganz gut. An was Aehnliches hatte ich auch schon gedacht, aber von vector abzuleiten ist eindeutig die Ueberlegene Strategie. Das werde ich mal versuchen.

Gruss

Thorsten

Hi Ben :smile:

Bei der von dir vorgeschlagenen Ableitung

template vectorIF : private vector

kann es zu Problemen kommen. Man darf nicht einfach so von einer Klasse ableiten, es gibt ein paar Minimalanforderungen, die man an die Mutterklasse richten muss. Die wichtigste ist wohl, dass vector einen virtuellen Destructor haben muss. Da du aber nicht weißt, wie vector in der STL implementiert ist, machst du vermutlich einen schlimmen Fehler (falls der Destructor nicht virtuell ist) oder/und erzeugst unportablen Code.

Viele Grüße

Stefan.

Hallo Stefan,

Hier wuerde ich widersprechen wollen:
Man benoetigt keinen virtuellen Destruktor wenn man von einer Klasse ableitet. Da hast du wohl etwas ein wenig durcheinander gewuerfelt.
Ein Objekt wird von unten nach oben konstruiert, und in umgekehrter Reihenfolge zerstoert. Das heisst, wenn du von einer Klasse ableitest wird deren Destruktor trotzdem noch aufgerufen. Einen virtuellen Destruktor sollte man dann verwenden, wenn man virtuelle Funktionen verwendet, denn in einem solchen Falle verwendet man statt einer Instanz, oder eines Pointers auf die abgeleitete Klasse haeufig ein Pointer auf die Basisklasse.
Gefaehrlich wird ein nichtvirtueller Destruktor erst, wenn man versucht die Klasse ueber einen Pointer auf eine der Basisklassen zu zerstoeren. Das wuerde dann dazu fuehren, das
die Destruktion mit dem Destruktor der Basisklasse ausgefuehrt wird, und so der Destruktor der abgeleiteten Klasse nicht aufgerufen wird.
Ich bin mir bezueglich meiner Aussage zwar nicht 100%ig sicher, aber so erscheint es mir am logischsten. Auf jeden Fall braucht man im Normalfall bei abgeleiteten Klassen die keine virtuellen Elementfunktionen enthalten keinen virtuellen Destruktor.

Gruss Ben

Hi Ben :smile:

Gefaehrlich wird ein nichtvirtueller Destruktor erst, wenn man
versucht die Klasse ueber einen Pointer auf eine der
Basisklassen zu zerstoeren.

Jupp, genau das meine ich! Im C+±Standard steht dazu sehr klar: Wenn Sie versuchen, ein Objekt einer abgeleiteten Klasse durch einen Basisklassen-Zeiger zu löschen und diese Basisklasse keinen virtuellen Konstruktor hat, dann ist das Ergebnis dieser Operation nicht definiert.

Ich bin mir bezueglich meiner Aussage zwar nicht 100%ig
sicher, aber so erscheint es mir am logischsten. Auf jeden
Fall braucht man im Normalfall bei abgeleiteten Klassen die
keine virtuellen Elementfunktionen enthalten keinen virtuellen
Destruktor.

Gerade weil man über die STL-Implementierung keine Annahmen machen sollte, ist die Vererbungsmethode daher riskant. Das führt normalerweise zu Fehlern, die man so gut wie nicht finden kann …

Viele Grüße

Stefan.

Selbst wenn die STL mit virtuellen Funktionen implementiert wird (was aber eher unwahrscheinlich ist, da dies erhoehte Laufzeitkosten zur folge haette) stellt das immer noch kein Problem dar wenn:
a) die abgeleitete Klasse keinen Destruktor benoetigt (was bei einer Interface Klasse im allgemeinen der Fall ist
b) auf die Klasse nicht ueber einen Basispointer zugegriffen wird, was auch nicht sinvoll waere, da sie ja eben nicht als virtuelle Klasse konzipiert ist, allerdings koennte das sinvoll sein, wenn man eine Funktion aufrufen moechte die fuer einen die Basisklasse konzipiert wurde (z.B. deleteVec(vector* pVec {delete pVec;})

Gerade nach Punkt a, aber auch nach Punkt b ist das erstellen einer Interfaceklasse, die von vector erbt aber wahrscheinlich ungefaehrlich (ich glaube Stroustrup macht dies in „Die Programmiersprache C++“ auch einmal), obwohl die Container-Klassen nicht als Basis fuer Ableitungen konzipiert wurden.

Gruss Ben

Hi Ben :smile:

Unter den von dir gemachten Einschränkungen insbesondere bezüglich Punkt b) ist es tatsächlich ungefährlich. Die Frage ist, ob man sich in einem halben Jahr noch daran erinnert, falls eine Erweiterung oder Modifikation an dem Source vorgenommen werden muss. Jeder muss selbst entscheiden, wie viel Risiko er in seinem Source haben möchte. Ich bin im Laufe der Jahre vielleicht zu vorsichtig geworden …

Viele Grüße

Stefan.