vector

Von: , Frage gestellt am Fr, 18. Mai 2001

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 <vector>
class A
{
private:
A(const A&) {}
public:
A() {}
friend vector<A>; // I could live with this
friend void construct<A, A> (A *, const A &);
// that construct thing might make the code unportable
};
void main()
{
vector<A> va;
//  vector<A&> 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 *,
ptrdiff_t>(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 *,
ptrdiff_t>(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

10 Antworten zu dieser Frage

  1. Antwort von nach einer Stunde 0 hilfreich
    Re: vector<class with private copy-constr/ refe

    Hi Thorsten :) 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.

    • Antwort von nach 2 Stunden 0 hilfreich
      Re^2: vector<class with private copy-constr/ re

      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 ,->):

      #include <iostream>
      template <class T> 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<int&> air(a);
      cout << air.readme() << endl; // 0
      a++;
      cout << air.readme() << endl; // 1
      }


      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 ;-) 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 ;-)

      Gruss

      Thorsten

      • Antwort von nach 3 Stunden 0 hilfreich
        Re^3: vector<class with private copy-constr/ re

        Hi Thorsten :) 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 :) 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 :) 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 ;-)
        Gar nicht! Dafür ist C++ die falsche Sprache.

        cu Stefan.

        • Antwort von nach 2 Tagen 0 hilfreich
          Re^4: vector<class with private copy-constr/ re

          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? ;-) 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 ;-)
          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 ;-)
          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

  2. Antwort von nach einem Tag 0 hilfreich
    Re: vector<class with private copy-constr/ refe

    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 <class T> vectorIF : private vector<T*>
    {
    // notwendige konstruktoren muessen angeboten werden, sollten inline sein, und an vector durchreichen
    T operator[](int a) { return *vector<T*>::operator[](a) } // ich habe das jetzt nicht getestet, aber theoretisch muesste es funktionieren
    push_back(T* a) { vector<T*>::push_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

    • Antwort von nach 2 Tagen 0 hilfreich
      Re^2: vector<class with private copy-constr/ re

      Hi Ben :)

      Bei der von dir vorgeschlagenen Ableitung template <class T> vectorIF : private vector<T*>
      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.

      • Antwort von nach 2 Tagen 0 hilfreich
        Re^3: vector<class with private copy-constr/ re

        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

        • Antwort von nach 3 Tagen 0 hilfreich
          Re^4: vector<class with private copy-constr/ re

          Hi Ben :) 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.

          • Antwort von nach 3 Tagen 0 hilfreich
            Re^5: vector<class with private copy-constr/ re

            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<T>* 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



Keine passende Antwort gefunden? Jetzt eigene Frage stellen!