Code aus meinem Bcuh funktioniert nicht
-
Warum geht der Code hier nicht?
#include <iostream> using namespace std; class Shape { public: Shape(); virtual ~Shape(); virtual long GetArea() const {return -1;} virtual long GetPerim() const {return -1;} virtual void Draw() const {}; }; Shape::Shape() { } Shape::~Shape() { } class Circle : public Shape { public: Circle(int radius); ~Circle(); long GetArea() const {return 3 * itsRadius * itsRadius;} long GetPerim() const {return 6 * itsRadius;} void Draw() const; private: int itsRadius; int itsCircumreference; }; Circle::Circle(int radius) :itsRadius(radius) { } Circle::~Circle() { } void Circle::Draw() const { cout << "Routine zum Zeichnen eins Kreises\n"; } class Rectangle { public: Rectangle(int len,int width); virtual ~Rectangle(); long GetArea() const {return itsLength * itsWidth;} long GetPerim() const {return 2*itsLength + 2*itsWidth;} int GetLength() const {return itsLength;} int GetWidth() const {return itsWidth;} void Draw() const; private: int itsLength; int itsWidth; }; Rectangle::Rectangle(int len,int width) :itsLength(len), itsWidth(width) { } Rectangle::~Rectangle() { } void Rectangle::Draw() const { for (int i=0; i<itsLength; i++) { for (int j=0; j<itsWidth; j++) { cout << "X"; } cout << "\n"; } } class Square : public Rectangle { public: Square(int len); Square(int len, int width); ~Square(); long GetPerim() const {return 4 * GetLength();} }; Square::Square(int len) :Rectangle(len,len) { } Square::Square(int len,int width) :Rectangle(len,width) { cout << "Fehler, kein Quadrat....ein Rechteck?\n"; } int main() { return 0; }Es kommt:
Compiling: C:\MinGW\Andi\kapitel 18.cpp
Linking console executable: C:\MinGW\Andi\kapitel 18.exe
C:\MinGW\Andi\kapitel 18.o:kapitel 18.cpp:(.text+0x3d6): undefined reference tovtable for Square' C:\\MinGW\\Andi\\kapitel 18.o:kapitel 18.cpp:(.text+0x400): undefined reference tovtable for Square'
C:\MinGW\Andi\kapitel 18.o:kapitel 18.cpp:(.text+0x42a): undefined reference tovtable for Square' C:\\MinGW\\Andi\\kapitel 18.o:kapitel 18.cpp:(.text+0x454): undefined reference tovtable for Square'
collect2: ld returned 1 exit status
Process terminated with status 1 (0 minutes, 1 seconds)
0 errors, 0 warningsMfG
Stromberg
-
Du hast den Destruktor der Klasse Square nicht definiert. (daß der Linker sich nicht über den fehlenden Dtor beschwert, liegt an den Eigenheiten des Compilers - der packt die vtable in die Datei mit der ersten virtuellen non-inline Methode der Klasse)
Btw, ist es eigentlich Absicht, daß Rectangle nicht von Shape abgeleitet ist?
-
CStoll schrieb:
...daß der Linker sich nicht über den fehlenden Dtor beschwert, liegt an den Eigenheiten des Compilers - der packt die vtable in die Datei mit der ersten virtuellen non-inline Methode der Klasse...
Ist aber schon eine selten dämliche Fehlermeldung, oder ?
Ich habe auch ganz schön gesucht, bis ich das gefunden habe..
@Stromberg: Damit einem sowas nicht passiert, schreibe ich immer erstmal gerne "triviale" Funktionen inline - da sieht man schneller, was noch fehlt.
Danach kann man einzelne Implementationen immer noch auslagern...Gruß,
Simon2.
-
Simon2 schrieb:
CStoll schrieb:
...daß der Linker sich nicht über den fehlenden Dtor beschwert, liegt an den Eigenheiten des Compilers - der packt die vtable in die Datei mit der ersten virtuellen non-inline Methode der Klasse...
Ist aber schon eine selten dämliche Fehlermeldung, oder ?
Ich habe auch ganz schön gesucht, bis ich das gefunden habe..
Schon möglich, aber damit muß man wohl leben (und ohne diese vtable geht's nicht).
-
#include <iostream> #include <cmath> class shape { public: shape() {}; virtual ~shape(){}; public: virtual double get_area() const = 0; virtual double get_perimeter() const = 0; virtual void draw(std::ostream&) const = 0; }; class circle : public shape { public: circle(double radius) : m_radius(radius) {} double get_area() const { return (std::acos(-1.0) * m_radius * m_radius); } double get_perimeter() const { return (2 * std::acos(-1.0) * m_radius); } void draw(std::ostream& out) const { out << "Kreis K(R: " << m_radius << ")"; } private: double m_radius; }; class rectangle : public shape { public: rectangle(double a, double b) : m_a(a), m_b(b) {} public: double get_area() const { return (m_a * m_b); } double get_perimeter() const { return (2 * m_a + 2 * m_b); } double get_a() const { return m_a; } double get_b() const { return m_b; } void draw(std::ostream& out) const { for(double a = 0; double a < m_a; a += 1.0) { for (double b = 0; b < m_b; b += 1.0) out << (a == 0 || a == (m_a - 1) ? "_" : "|"); out << std::endl; } } private: double m_a; double m_b; }; class square : public rectangle { public: square(double a) : rectangle(a, a) {} };
So ist das doch schon hübsch 
-
CStoll schrieb:
...Schon möglich, aber damit muß man wohl leben (und ohne diese vtable geht's nicht).
Mir leuchtet halt das Vorgehen des Compilers hier nicht ein. Wieso moniert er eine fehlende vtable an und warum fehlt sie, wenn die Implementation EINER Memberfunktion fehlt (was er ja als Letztes feststellt) ?
EDIT: AAAAAH - jetzt hab ich's verstanden !
Der Compiler legt die vtable in dem Modul an, in dem er die erste nicht-inline-Implementierung findet ....
Der Linker möchte die Implementierungen dort "reinhängen" - und dazu braucht er die vtable.Was passiert eigentlich, wenn man dem Linker in mehreren Modulen eine vtable anbietet ? (wenn man die Implementierungen auf verschiedene cpp's verteilt, die man getrennt compiliert)
Stellt er dann "Identität" fest und entscheidet sich einfach für eine ?Gruß,
Simon2.
-
Für die vtable gilt die ODR genauso wie für jedes andere Objekt und darum muß der Compiler einen Punkt finden, wo er sie definieren kann. Und das passiert dann normalerweise in der Übersetzungseinheit, in der die erste virtuelle Methode der Klasse untergebracht ist. Das heißt, wenn genau diese Methode nicht definiert wurde, fehlt (auch) die Definition der vtable.
(und für den Linker ist die vtable wichtiger als die nicht definierte Methode, deshalb beschwert er sich darüber, daß sie fehlt)
Edit:
Simon2 schrieb:
Was passiert eigentlich, wenn man dem Linker in mehreren Modulen eine vtable anbietet ? (wenn man die Implementierungen auf verschiedene cpp's verteilt, die man getrennt compiliert)
Stellt er dann "Identität" fest und entscheidet sich einfach für eine ?Es gibt genau eine "erste" Methode - und wenn die doppelt vorkommt, wird der Linker sich darüber vermutlich auch beschweren. Kritisch wird es imho nur, wenn du mit expliziten inline-Angaben rumspielst - aber vermutlich endet das auch in Linkerfehlern.
-
CStoll schrieb:
...Es gibt genau eine "erste" Methode - und wenn die doppelt vorkommt, wird der Linker sich darüber vermutlich auch beschweren. ..
Mir schwebte Folgendes vor:
// MyFatClass.h class MyFatClass { // ... säckeweise Funktionsdeklarationen };// MyFatClass_impl1.cpp #include "MyFatClass.h" void MyFatClass::f1() { } // erste void MyFatClass::f2() { } // ... säckeweise Funktionsdefinitionen// MyFatClass_impl2.cpp #include "MyFatClass.h" void MyFatClass::f1001() { } // erste void MyFatClass::f1002() { } // ... säckeweise FunktionsdefinitionenWenn ich nun die beiden cpps getrennt compiliere, stellt der Compiler für jede einmal "eine erste" fest und sollte in beiden objs eine vtable anlegen, die dann der Linker beide findet.
Das ist doch kein sooo exotisches Szenario, dass es bereits die Standardvorgaben sprengt, oder ?Meine Vorstellung ist eigentlich eher so eine "Identitätsregel für ODR" wie es sie auch bei templates gibt...
Gruß,
Simon2.
-
Nein, es geht nicht um die erste "hier" definierte Methode, sondern um die erste Methode in der Klassendeklaration - und die ist normalerweise überall die selbe.
Problematisch könnte nur folgendes werden:
//test.h class test { virtual void f1();//unter normalen Umständen ist das hier die erste Methode virtual void f2(); virtual void f3(); ... }; //test1.cpp #include "test.h" inline void test::f1(){...}//wurde als inline deklariert, also ist f2 jetzt erste Methode void test::f3(){...} //test2.cpp #include "test.h" void test::f2(){...} //hier weiß der Compiler nichts von dem obigen inline, also erwartet er die vtable in der test1.cpp
-
Ach so !!!

Danke.
Zu Deinem Beispiel: Mein gcc wirft dann gar ein "undefined symbol f1()" raus...
Gruß,
Simon2.
-
Ja, das kann auch vorkommen - schließlich ist f1() außerhalb der test1.cpp nicht zu erreichen (inline funktioniert nur dann, wenn der Quellcode in jeder ÜE sichtbar ist, die ihn benötigt).